Commit a1f6b323e3258211310b5229cbd8cabb83d491d9
Merge branch 'master_dev_gbt28181' into 'master_dev'
feat: 新增gbt28181(支持点播、云台控制) See merge request yunteng/thingskit!339
Showing
80 changed files
with
4464 additions
and
48 deletions
Too many changes to show.
To preserve performance only 80 of 155 files are displayed.
@@ -92,6 +92,10 @@ | @@ -92,6 +92,10 @@ | ||
92 | </dependency> | 92 | </dependency> |
93 | <dependency> | 93 | <dependency> |
94 | <groupId>org.thingsboard.common.transport</groupId> | 94 | <groupId>org.thingsboard.common.transport</groupId> |
95 | + <artifactId>gbt28181</artifactId> | ||
96 | + </dependency> | ||
97 | + <dependency> | ||
98 | + <groupId>org.thingsboard.common.transport</groupId> | ||
95 | <artifactId>http</artifactId> | 99 | <artifactId>http</artifactId> |
96 | </dependency> | 100 | </dependency> |
97 | <dependency> | 101 | <dependency> |
@@ -74,7 +74,7 @@ public class ThingsboardSecurityConfiguration extends WebSecurityConfigurerAdapt | @@ -74,7 +74,7 @@ public class ThingsboardSecurityConfiguration extends WebSecurityConfigurerAdapt | ||
74 | 74 | ||
75 | //Thingskit function | 75 | //Thingskit function |
76 | public static final String CODE_BASED_LOGIN_ENTRY_POINT = "/api/yt/auth/code/login"; | 76 | public static final String CODE_BASED_LOGIN_ENTRY_POINT = "/api/yt/auth/code/login"; |
77 | - public static final String[] YT_NOT_AUTH_API = new String[]{"/api/yt/auth/code/login","/api/yt/third/bind","/api/yt/third/login/*","/api/yt/third/login/id/*", "/api/yt/third/authorize","/api/yt/platform/get","/api/yt/app_design/get", "/api/yt/noauth/**"}; | 77 | + public static final String[] YT_NOT_AUTH_API = new String[]{"/api/yt/auth/code/login","/api/yt/third/bind","/api/yt/third/login/*","/api/yt/third/login/id/*", "/api/yt/third/authorize","/api/yt/platform/get","/api/yt/app_design/get", "/api/yt/noauth/**","/api/index/hook/**"}; |
78 | 78 | ||
79 | public static final String PUBLIC_LOGIN_ENTRY_POINT = "/api/auth/login/public"; | 79 | public static final String PUBLIC_LOGIN_ENTRY_POINT = "/api/auth/login/public"; |
80 | public static final String TOKEN_REFRESH_ENTRY_POINT = "/api/auth/token"; | 80 | public static final String TOKEN_REFRESH_ENTRY_POINT = "/api/auth/token"; |
@@ -82,7 +82,7 @@ public class ThingsKitExceptionHandler { | @@ -82,7 +82,7 @@ public class ThingsKitExceptionHandler { | ||
82 | SecurityUser currentUser = getCurrentUser(); | 82 | SecurityUser currentUser = getCurrentUser(); |
83 | 83 | ||
84 | Asset entity = new Asset(); | 84 | Asset entity = new Asset(); |
85 | - entity.setName(e.getMessage()); | 85 | + entity.setName(e.getClass().getName()); |
86 | 86 | ||
87 | // 请求相关信息 | 87 | // 请求相关信息 |
88 | SysLogOperateDTO additionalInfo = new SysLogOperateDTO(); | 88 | SysLogOperateDTO additionalInfo = new SysLogOperateDTO(); |
application/src/main/java/org/thingsboard/server/controller/yunteng/TkVideoChannelController.java
0 → 100644
1 | +package org.thingsboard.server.controller.yunteng; | ||
2 | + | ||
3 | +import io.swagger.annotations.Api; | ||
4 | +import io.swagger.annotations.ApiParam; | ||
5 | +import lombok.RequiredArgsConstructor; | ||
6 | +import org.springframework.security.access.prepost.PreAuthorize; | ||
7 | +import org.springframework.web.bind.annotation.GetMapping; | ||
8 | +import org.springframework.web.bind.annotation.RequestMapping; | ||
9 | +import org.springframework.web.bind.annotation.RequestParam; | ||
10 | +import org.springframework.web.bind.annotation.RestController; | ||
11 | +import org.thingsboard.server.common.data.StringUtils; | ||
12 | +import org.thingsboard.server.common.data.exception.ThingsboardException; | ||
13 | +import org.thingsboard.server.common.data.yunteng.core.exception.TkDataValidationException; | ||
14 | +import org.thingsboard.server.common.data.yunteng.core.message.ErrorMessage; | ||
15 | +import org.thingsboard.server.common.data.yunteng.dto.DeviceDTO; | ||
16 | +import org.thingsboard.server.common.data.yunteng.dto.sip.VideoChanelDTO; | ||
17 | +import org.thingsboard.server.common.data.yunteng.enums.OrderTypeEnum; | ||
18 | +import org.thingsboard.server.common.data.yunteng.utils.tools.TkPageData; | ||
19 | +import org.thingsboard.server.controller.BaseController; | ||
20 | +import org.thingsboard.server.dao.yunteng.mapper.DeviceMapper; | ||
21 | +import org.thingsboard.server.dao.yunteng.service.TkDeviceService; | ||
22 | +import org.thingsboard.server.dao.yunteng.service.media.TkVideoChannelService; | ||
23 | + | ||
24 | +import java.util.HashMap; | ||
25 | + | ||
26 | +import static org.thingsboard.server.common.data.yunteng.constant.QueryConstant.*; | ||
27 | +import static org.thingsboard.server.common.data.yunteng.constant.QueryConstant.ORDER_TYPE; | ||
28 | + | ||
29 | +@RestController | ||
30 | +@RequestMapping("api/yt/video/channel") | ||
31 | +@Api(tags = {"视频通道"}) | ||
32 | +@RequiredArgsConstructor | ||
33 | +public class TkVideoChannelController extends BaseController { | ||
34 | + | ||
35 | + private final TkVideoChannelService tkVideoChannelService; | ||
36 | + private final TkDeviceService tkDeviceService; | ||
37 | + private final DeviceMapper deviceMapper; | ||
38 | + | ||
39 | + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") | ||
40 | + @GetMapping(params = {PAGE_SIZE, PAGE}) | ||
41 | + public TkPageData<VideoChanelDTO> pageData( | ||
42 | + @RequestParam(PAGE_SIZE) int pageSize, | ||
43 | + @RequestParam(PAGE) int page, | ||
44 | + @RequestParam(value = ORDER_FILED, required = false) String orderBy, | ||
45 | + @RequestParam(value = ORDER_TYPE, required = false) OrderTypeEnum orderType, | ||
46 | + @ApiParam(value = "通道所属设备ID(tbDeviceId)") | ||
47 | + @RequestParam(value = "tbDeviceId") String tbDeviceId, | ||
48 | + @ApiParam(value = "视频通道名称") | ||
49 | + @RequestParam(value = "name", required = false) String name, | ||
50 | + @ApiParam(value = "国标编号") | ||
51 | + @RequestParam(value = "cameraCode", required = false) String cameraCode) throws ThingsboardException { | ||
52 | + HashMap<String, Object> queryMap = new HashMap<>(); | ||
53 | + if(StringUtils.isNotEmpty(name)){ | ||
54 | + queryMap.put("name",name); | ||
55 | + } | ||
56 | + if(StringUtils.isNotEmpty(cameraCode)){ | ||
57 | + queryMap.put("cameraCode",cameraCode); | ||
58 | + } | ||
59 | + String tenantId = getCurrentUser().getCurrentTenantId(); | ||
60 | + if(StringUtils.isEmpty(tbDeviceId)){ | ||
61 | + throw new TkDataValidationException(ErrorMessage.INVALID_PARAMETER.getMessage()); | ||
62 | + } | ||
63 | + if(getCurrentUser().isCustomerUser()){ | ||
64 | + DeviceDTO deviceDTO = deviceMapper.findDeviceByTbDeviceIdAndCustomerId( | ||
65 | + tenantId,tbDeviceId,getCurrentUser().getCustomerId().toString()); | ||
66 | + if(null == deviceDTO){ | ||
67 | + throw new TkDataValidationException(ErrorMessage.NOT_BELONG_CURRENT_CUSTOMER.getMessage()); | ||
68 | + } | ||
69 | + } | ||
70 | + queryMap.put("tbDeviceId",tbDeviceId); | ||
71 | + queryMap.put(PAGE_SIZE, pageSize); | ||
72 | + queryMap.put(PAGE, page); | ||
73 | + queryMap.put(ORDER_FILED, orderBy); | ||
74 | + if (orderType != null) { | ||
75 | + queryMap.put(ORDER_TYPE, orderType.name()); | ||
76 | + } | ||
77 | + return tkVideoChannelService.page(tenantId, queryMap); | ||
78 | + } | ||
79 | +} |
@@ -11,6 +11,7 @@ import org.thingsboard.server.common.data.exception.ThingsboardException; | @@ -11,6 +11,7 @@ import org.thingsboard.server.common.data.exception.ThingsboardException; | ||
11 | import org.thingsboard.server.common.data.yunteng.common.DeleteGroup; | 11 | import org.thingsboard.server.common.data.yunteng.common.DeleteGroup; |
12 | import org.thingsboard.server.common.data.yunteng.dto.DeleteDTO; | 12 | import org.thingsboard.server.common.data.yunteng.dto.DeleteDTO; |
13 | import org.thingsboard.server.common.data.yunteng.dto.TkVideoDTO; | 13 | import org.thingsboard.server.common.data.yunteng.dto.TkVideoDTO; |
14 | +import org.thingsboard.server.common.data.yunteng.dto.TkVideoGptDTO; | ||
14 | import org.thingsboard.server.common.data.yunteng.enums.OrderTypeEnum; | 15 | import org.thingsboard.server.common.data.yunteng.enums.OrderTypeEnum; |
15 | import org.thingsboard.server.common.data.yunteng.utils.tools.ProtocolType; | 16 | import org.thingsboard.server.common.data.yunteng.utils.tools.ProtocolType; |
16 | import org.thingsboard.server.common.data.yunteng.utils.tools.ResponseResult; | 17 | import org.thingsboard.server.common.data.yunteng.utils.tools.ResponseResult; |
@@ -68,6 +69,17 @@ public class TkVideoController extends BaseController { | @@ -68,6 +69,17 @@ public class TkVideoController extends BaseController { | ||
68 | return videoService.saveOrUpdate(dto); | 69 | return videoService.saveOrUpdate(dto); |
69 | } | 70 | } |
70 | 71 | ||
72 | + @PostMapping("/addGpt") | ||
73 | + @ApiOperation("新增国标视频") | ||
74 | + @PreAuthorize( | ||
75 | + "@check.checkPermissions({'TENANT_ADMIN','CUSTOMER_USER'},{'api:yt:video:post','api:yt:video:update'})") | ||
76 | + public TkVideoGptDTO saveOrUpdateAlarmProfile(@Validated @RequestBody TkVideoGptDTO dto) | ||
77 | + throws ThingsboardException { | ||
78 | + dto.setTenantId(getCurrentUser().getCurrentTenantId()); | ||
79 | + return videoService.saveGpt(dto); | ||
80 | + } | ||
81 | + | ||
82 | + | ||
71 | @DeleteMapping | 83 | @DeleteMapping |
72 | @ApiOperation("删除") | 84 | @ApiOperation("删除") |
73 | @PreAuthorize("@check.checkPermissions({'TENANT_ADMIN','CUSTOMER_USER'},{'api:yt:video:delete'})") | 85 | @PreAuthorize("@check.checkPermissions({'TENANT_ADMIN','CUSTOMER_USER'},{'api:yt:video:delete'})") |
1 | +package org.thingsboard.server.controller.yunteng.media; | ||
2 | + | ||
3 | +import com.google.common.util.concurrent.MoreExecutors; | ||
4 | +import io.swagger.annotations.Api; | ||
5 | +import io.swagger.annotations.ApiOperation; | ||
6 | +import io.swagger.annotations.ApiParam; | ||
7 | +import io.swagger.v3.oas.annotations.Parameter; | ||
8 | +import lombok.RequiredArgsConstructor; | ||
9 | +import lombok.extern.slf4j.Slf4j; | ||
10 | +import org.apache.curator.shaded.com.google.common.util.concurrent.FutureCallback; | ||
11 | +import org.apache.curator.shaded.com.google.common.util.concurrent.Futures; | ||
12 | +import org.apache.curator.shaded.com.google.common.util.concurrent.ListenableFuture; | ||
13 | +import org.checkerframework.checker.nullness.qual.Nullable; | ||
14 | +import org.jetbrains.annotations.NotNull; | ||
15 | +import org.springframework.web.bind.annotation.*; | ||
16 | +import org.springframework.web.context.request.async.DeferredResult; | ||
17 | +import org.thingsboard.server.common.data.StringUtils; | ||
18 | +import org.thingsboard.server.common.data.exception.ThingsboardException; | ||
19 | +import org.thingsboard.server.common.data.yunteng.core.exception.TkDataValidationException; | ||
20 | +import org.thingsboard.server.common.data.yunteng.core.message.ErrorMessage; | ||
21 | +import org.thingsboard.server.common.data.yunteng.dto.DeviceDTO; | ||
22 | +import org.thingsboard.server.common.data.yunteng.dto.sip.StreamContentDTO; | ||
23 | +import org.thingsboard.server.common.data.yunteng.enums.PTZCommandEnum; | ||
24 | +import org.thingsboard.server.common.data.yunteng.utils.tools.ResponseResult; | ||
25 | +import org.thingsboard.server.controller.BaseController; | ||
26 | +import org.thingsboard.server.dao.yunteng.service.TkDeviceService; | ||
27 | +import org.thingsboard.server.service.yunteng.media.TkVideoControlService; | ||
28 | + | ||
29 | +@RestController | ||
30 | +@RequestMapping("api/yt/video/control") | ||
31 | +@Api(tags = {"视频控制管理"}) | ||
32 | +@RequiredArgsConstructor | ||
33 | +@Slf4j | ||
34 | +public class TkVideoControlController extends BaseController { | ||
35 | + | ||
36 | + private final TkVideoControlService tkVideoControlService; | ||
37 | + private final TkDeviceService tkDeviceService; | ||
38 | + | ||
39 | + @GetMapping("/start/{deviceId}/{channelId}") | ||
40 | + @ApiOperation(value = "视频点播/预览") | ||
41 | + public DeferredResult<ResponseResult<StreamContentDTO>> startPlay( | ||
42 | + @PathVariable("deviceId") String tbDeviceId, @PathVariable("channelId") String channelId) | ||
43 | + throws ThingsboardException { | ||
44 | + if (StringUtils.isEmpty(tbDeviceId) || StringUtils.isEmpty(channelId)) { | ||
45 | + throw new TkDataValidationException(ErrorMessage.INVALID_PARAMETER.getMessage()); | ||
46 | + } | ||
47 | + DeferredResult<ResponseResult<StreamContentDTO>> response = new DeferredResult<>(); | ||
48 | + ListenableFuture<StreamContentDTO> future = | ||
49 | + tkVideoControlService.startPlay(getCurrentUser(),tbDeviceId, channelId, getCurrentUser().getCurrentTenantId()); | ||
50 | + Futures.addCallback( | ||
51 | + future, | ||
52 | + new FutureCallback<>() { | ||
53 | + @Override | ||
54 | + public void onSuccess(@Nullable StreamContentDTO streamContentDTO) { | ||
55 | + response.setResult(ResponseResult.success(streamContentDTO)); | ||
56 | + } | ||
57 | + | ||
58 | + @Override | ||
59 | + public void onFailure(@NotNull Throwable throwable) { | ||
60 | + response.setResult(ResponseResult.failed(null)); | ||
61 | + } | ||
62 | + }, | ||
63 | + MoreExecutors.directExecutor()); | ||
64 | + return response; | ||
65 | + } | ||
66 | + | ||
67 | + @ApiOperation(value = "停止点播") | ||
68 | + @GetMapping("/stop/{deviceId}/{channelId}") | ||
69 | + public ResponseResult playStop( | ||
70 | + @ApiParam(value = "设备ID", required = true) @PathVariable("deviceId") String tbDeviceId, | ||
71 | + @ApiParam(value = "通道ID", required = true) @PathVariable("channelId") String channelId) | ||
72 | + throws ThingsboardException { | ||
73 | + if (StringUtils.isEmpty(tbDeviceId) || StringUtils.isEmpty(channelId)) { | ||
74 | + throw new TkDataValidationException(ErrorMessage.INVALID_PARAMETER.getMessage()); | ||
75 | + } | ||
76 | + return ResponseResult.success( | ||
77 | + tkVideoControlService.stopPlay(getCurrentUser(),tbDeviceId, channelId)); | ||
78 | + } | ||
79 | + | ||
80 | + @ApiOperation(value = "云台控制") | ||
81 | + @Parameter(name = "tbDeviceId", description = "TB设备ID", required = true) | ||
82 | + @Parameter(name = "channelId", description = "通道国标编号", required = true) | ||
83 | + @Parameter( | ||
84 | + name = "command", | ||
85 | + description = | ||
86 | + "控制指令,允许值: LEFT, RIGHT, UP, DOWN, UP_LEFT, UPRIGHT, DOWN_LEFT, DOWN_RIGHT, ZOOM_IN, ZOOM_OUT, STOP", | ||
87 | + required = true) | ||
88 | + @Parameter(name = "horizonSpeed", description = "水平速度", required = true) | ||
89 | + @Parameter(name = "verticalSpeed", description = "垂直速度", required = true) | ||
90 | + @Parameter(name = "zoomSpeed", description = "缩放速度", required = true) | ||
91 | + @GetMapping("/control/{tbDeviceId}/{channelId}") | ||
92 | + public void ptzControl( | ||
93 | + @PathVariable String tbDeviceId, | ||
94 | + @PathVariable String channelId, | ||
95 | + PTZCommandEnum command, | ||
96 | + int horizonSpeed, | ||
97 | + int verticalSpeed, | ||
98 | + int zoomSpeed) | ||
99 | + throws ThingsboardException { | ||
100 | + ResponseResult.success( | ||
101 | + tkVideoControlService.control(getCurrentUser(),tbDeviceId, channelId,command,horizonSpeed,verticalSpeed,zoomSpeed)); | ||
102 | + } | ||
103 | +} |
1 | +package org.thingsboard.server.controller.yunteng.media; | ||
2 | + | ||
3 | +import com.fasterxml.jackson.databind.JsonNode; | ||
4 | +import io.swagger.annotations.Api; | ||
5 | +import io.swagger.annotations.ApiOperation; | ||
6 | +import lombok.RequiredArgsConstructor; | ||
7 | +import lombok.extern.slf4j.Slf4j; | ||
8 | +import org.springframework.web.bind.annotation.*; | ||
9 | +import org.thingsboard.server.common.data.exception.ThingsboardException; | ||
10 | +import org.thingsboard.server.common.data.yunteng.constant.FastIotConstants; | ||
11 | +import org.thingsboard.server.common.data.yunteng.dto.DeviceDTO; | ||
12 | +import org.thingsboard.server.common.data.yunteng.dto.sip.VideoChanelDTO; | ||
13 | +import org.thingsboard.server.common.data.yunteng.dto.sip.hook.param.*; | ||
14 | +import org.thingsboard.server.controller.BaseController; | ||
15 | +import org.thingsboard.server.dao.yunteng.service.TkDeviceService; | ||
16 | +import org.thingsboard.server.dao.yunteng.service.media.TkMediaServerService; | ||
17 | +import org.thingsboard.server.dao.yunteng.service.media.TkVideoChannelService; | ||
18 | +import org.thingsboard.server.service.yunteng.media.TkVideoControlService; | ||
19 | + | ||
20 | +/** ZLMediaServer的hook事件监听 */ | ||
21 | +@RestController | ||
22 | +@RequestMapping("api/index/hook") | ||
23 | +@Api(tags = {"ZLMediaServer的hook事件监听"}) | ||
24 | +@RequiredArgsConstructor | ||
25 | +@Slf4j | ||
26 | +public class ZLMediaKitHookController extends BaseController { | ||
27 | + private final TkMediaServerService tkMediaServerService; | ||
28 | + private final TkVideoChannelService tkVideoChannelService; | ||
29 | + private final TkVideoControlService controlService; | ||
30 | + | ||
31 | + @PostMapping(value = "/on_server_keepalive", produces = "application/json;charset=UTF-8") | ||
32 | + @ApiOperation(value = "保持流媒体服务器的心跳") | ||
33 | + public HookResult onServerKeepalive(@RequestBody OnServerKeepaliveHookParam param) { | ||
34 | + // log.error("ZLMediaKitHook保持流媒体服务器的心跳【on_server_keepalive】,API参数【{}】", | ||
35 | + // JacksonUtil.toString(param)); | ||
36 | + return tkMediaServerService.zlmOnServerKeepalive(param); | ||
37 | + } | ||
38 | + | ||
39 | + @PostMapping(value = "/on_play", produces = "application/json;charset=UTF-8") | ||
40 | + @ApiOperation(value = "播放器鉴权事件") | ||
41 | + public HookResult onPlay(@RequestBody BaseParam param) { | ||
42 | + return tkMediaServerService.zlmOnPlay(param); | ||
43 | + } | ||
44 | + | ||
45 | + @PostMapping(value = "/on_publish", produces = "application/json;charset=UTF-8") | ||
46 | + @ApiOperation(value = "rtsp/rtmp/rtp推流鉴权事件") | ||
47 | + public HookResultForOnPublish onPublish(@RequestBody BaseParam param) { | ||
48 | + return tkMediaServerService.onPublish(param); | ||
49 | + } | ||
50 | + | ||
51 | + @PostMapping(value = "/on_stream_changed", produces = "application/json;charset=UTF-8") | ||
52 | + @ApiOperation(value = "rtsp/rtmp流注册或注销时触发此事件;此事件对回复不敏感") | ||
53 | + public HookResult onStreamChanged(@RequestBody OnStreamChangedHookParam param) { | ||
54 | + return tkMediaServerService.zlmOnStreamChanged(param); | ||
55 | + } | ||
56 | + | ||
57 | + @ResponseBody | ||
58 | + @PostMapping(value = "/on_stream_none_reader", produces = "application/json;charset=UTF-8") | ||
59 | + @ApiOperation(value = "流无人观看时事件,用户可以通过此事件选择是否关闭无人看的流") | ||
60 | + public JsonNode onStreamNoneReader(@RequestBody OnStreamNoneReaderHookParam param) throws ThingsboardException { | ||
61 | + JsonNode result = tkMediaServerService.zlmOnStreamNoneReader(param); | ||
62 | + if(result.has(FastIotConstants.ZLMediaBody.CHANNEL_ID) &&result.has(FastIotConstants.ZLMediaBody.CAMERA_CODE)){ | ||
63 | + String channelId = result.get(FastIotConstants.ZLMediaBody.CHANNEL_ID).asText(); | ||
64 | + String cameraCode = result.get(FastIotConstants.ZLMediaBody.CAMERA_CODE).asText(); | ||
65 | + VideoChanelDTO chanelDTO = tkVideoChannelService.findVideoChannelById(cameraCode,channelId, null); | ||
66 | + controlService.byeCmdInSsrcTransaction(chanelDTO.getTenantId(), | ||
67 | + false, | ||
68 | + chanelDTO.getDeviceId(), | ||
69 | + cameraCode,channelId,result.get(FastIotConstants.ZLMediaBody.SSRCINFO_STREAM).asText(), | ||
70 | + fromDeviceRpcResponse->{ | ||
71 | + | ||
72 | + }); | ||
73 | + tkVideoChannelService.updateVideoChannelStreamId(null, cameraCode, channelId);//storager.stopPlay(streamInfo.getDeviceID(), streamInfo.getChannelId()); | ||
74 | + } | ||
75 | + return result; | ||
76 | + } | ||
77 | +} |
@@ -15,6 +15,9 @@ | @@ -15,6 +15,9 @@ | ||
15 | */ | 15 | */ |
16 | package org.thingsboard.server.service.transport; | 16 | package org.thingsboard.server.service.transport; |
17 | 17 | ||
18 | +import static org.thingsboard.server.service.transport.BasicCredentialsValidationResult.PASSWORD_MISMATCH; | ||
19 | +import static org.thingsboard.server.service.transport.BasicCredentialsValidationResult.VALID; | ||
20 | + | ||
18 | import com.fasterxml.jackson.core.JsonProcessingException; | 21 | import com.fasterxml.jackson.core.JsonProcessingException; |
19 | import com.fasterxml.jackson.databind.JsonNode; | 22 | import com.fasterxml.jackson.databind.JsonNode; |
20 | import com.fasterxml.jackson.databind.ObjectMapper; | 23 | import com.fasterxml.jackson.databind.ObjectMapper; |
@@ -23,8 +26,18 @@ import com.google.common.util.concurrent.Futures; | @@ -23,8 +26,18 @@ import com.google.common.util.concurrent.Futures; | ||
23 | import com.google.common.util.concurrent.ListenableFuture; | 26 | import com.google.common.util.concurrent.ListenableFuture; |
24 | import com.google.common.util.concurrent.MoreExecutors; | 27 | import com.google.common.util.concurrent.MoreExecutors; |
25 | import com.google.protobuf.ByteString; | 28 | import com.google.protobuf.ByteString; |
29 | +import java.util.List; | ||
30 | +import java.util.Optional; | ||
31 | +import java.util.Set; | ||
32 | +import java.util.UUID; | ||
33 | +import java.util.concurrent.ConcurrentHashMap; | ||
34 | +import java.util.concurrent.ConcurrentMap; | ||
35 | +import java.util.concurrent.locks.Lock; | ||
36 | +import java.util.concurrent.locks.ReentrantLock; | ||
37 | +import java.util.stream.Collectors; | ||
26 | import lombok.RequiredArgsConstructor; | 38 | import lombok.RequiredArgsConstructor; |
27 | import lombok.extern.slf4j.Slf4j; | 39 | import lombok.extern.slf4j.Slf4j; |
40 | +import org.apache.zookeeper.Op; | ||
28 | import org.springframework.stereotype.Service; | 41 | import org.springframework.stereotype.Service; |
29 | import org.thingsboard.common.util.JacksonUtil; | 42 | import org.thingsboard.common.util.JacksonUtil; |
30 | import org.thingsboard.server.cache.ota.OtaPackageDataCache; | 43 | import org.thingsboard.server.cache.ota.OtaPackageDataCache; |
@@ -60,9 +73,14 @@ import org.thingsboard.server.common.data.page.PageLink; | @@ -60,9 +73,14 @@ import org.thingsboard.server.common.data.page.PageLink; | ||
60 | import org.thingsboard.server.common.data.relation.EntityRelation; | 73 | import org.thingsboard.server.common.data.relation.EntityRelation; |
61 | import org.thingsboard.server.common.data.security.DeviceCredentials; | 74 | import org.thingsboard.server.common.data.security.DeviceCredentials; |
62 | import org.thingsboard.server.common.data.security.DeviceCredentialsType; | 75 | import org.thingsboard.server.common.data.security.DeviceCredentialsType; |
76 | +import org.thingsboard.server.common.data.yunteng.common.media.VideoStreamSessionManager; | ||
77 | +import org.thingsboard.server.common.data.yunteng.constant.FastIotConstants; | ||
63 | import org.thingsboard.server.common.data.yunteng.dto.DeviceDTO; | 78 | import org.thingsboard.server.common.data.yunteng.dto.DeviceDTO; |
64 | import org.thingsboard.server.common.data.yunteng.dto.TkDeviceScriptDTO; | 79 | import org.thingsboard.server.common.data.yunteng.dto.TkDeviceScriptDTO; |
65 | -import org.thingsboard.server.common.data.yunteng.enums.TkScriptFunctionType; | 80 | +import org.thingsboard.server.common.data.yunteng.dto.sip.SipDeviceDTO; |
81 | +import org.thingsboard.server.common.data.yunteng.dto.sip.SsrcTransactionDTO; | ||
82 | +import org.thingsboard.server.common.data.yunteng.dto.sip.VideoChanelDTO; | ||
83 | +import org.thingsboard.server.common.data.yunteng.enums.VideoCmdEnum; | ||
66 | import org.thingsboard.server.common.msg.EncryptionUtil; | 84 | import org.thingsboard.server.common.msg.EncryptionUtil; |
67 | import org.thingsboard.server.common.msg.TbMsg; | 85 | import org.thingsboard.server.common.msg.TbMsg; |
68 | import org.thingsboard.server.common.msg.TbMsgDataType; | 86 | import org.thingsboard.server.common.msg.TbMsgDataType; |
@@ -79,6 +97,8 @@ import org.thingsboard.server.dao.relation.RelationService; | @@ -79,6 +97,8 @@ import org.thingsboard.server.dao.relation.RelationService; | ||
79 | import org.thingsboard.server.dao.tenant.TbTenantProfileCache; | 97 | import org.thingsboard.server.dao.tenant.TbTenantProfileCache; |
80 | import org.thingsboard.server.dao.yunteng.service.TkDeviceScriptService; | 98 | import org.thingsboard.server.dao.yunteng.service.TkDeviceScriptService; |
81 | import org.thingsboard.server.dao.yunteng.service.TkDeviceService; | 99 | import org.thingsboard.server.dao.yunteng.service.TkDeviceService; |
100 | +import org.thingsboard.server.dao.yunteng.service.media.TkMediaServerNodeService; | ||
101 | +import org.thingsboard.server.dao.yunteng.service.media.TkVideoChannelService; | ||
82 | import org.thingsboard.server.gen.transport.TransportProtos; | 102 | import org.thingsboard.server.gen.transport.TransportProtos; |
83 | import org.thingsboard.server.gen.transport.TransportProtos.DeviceInfoProto; | 103 | import org.thingsboard.server.gen.transport.TransportProtos.DeviceInfoProto; |
84 | import org.thingsboard.server.gen.transport.TransportProtos.GetDeviceCredentialsRequestMsg; | 104 | import org.thingsboard.server.gen.transport.TransportProtos.GetDeviceCredentialsRequestMsg; |
@@ -105,19 +125,6 @@ import org.thingsboard.server.service.install.DefaultSystemDataLoaderService; | @@ -105,19 +125,6 @@ import org.thingsboard.server.service.install.DefaultSystemDataLoaderService; | ||
105 | import org.thingsboard.server.service.profile.TbDeviceProfileCache; | 125 | import org.thingsboard.server.service.profile.TbDeviceProfileCache; |
106 | import org.thingsboard.server.service.resource.TbResourceService; | 126 | import org.thingsboard.server.service.resource.TbResourceService; |
107 | 127 | ||
108 | -import java.util.Arrays; | ||
109 | -import java.util.List; | ||
110 | -import java.util.Optional; | ||
111 | -import java.util.UUID; | ||
112 | -import java.util.concurrent.ConcurrentHashMap; | ||
113 | -import java.util.concurrent.ConcurrentMap; | ||
114 | -import java.util.concurrent.locks.Lock; | ||
115 | -import java.util.concurrent.locks.ReentrantLock; | ||
116 | -import java.util.stream.Collectors; | ||
117 | - | ||
118 | -import static org.thingsboard.server.service.transport.BasicCredentialsValidationResult.PASSWORD_MISMATCH; | ||
119 | -import static org.thingsboard.server.service.transport.BasicCredentialsValidationResult.VALID; | ||
120 | - | ||
121 | /** | 128 | /** |
122 | * Created by ashvayka on 05.10.18. | 129 | * Created by ashvayka on 05.10.18. |
123 | */ | 130 | */ |
@@ -135,6 +142,10 @@ public class DefaultTransportApiService implements TransportApiService { | @@ -135,6 +142,10 @@ public class DefaultTransportApiService implements TransportApiService { | ||
135 | private final DeviceService deviceService; | 142 | private final DeviceService deviceService; |
136 | private final TkDeviceService ytDeviceService; | 143 | private final TkDeviceService ytDeviceService; |
137 | private final TkDeviceScriptService scriptService; | 144 | private final TkDeviceScriptService scriptService; |
145 | + private final TkVideoChannelService channelService; | ||
146 | + private final TkMediaServerNodeService mediaServerNodeService; | ||
147 | + private final VideoStreamSessionManager videoStreamSessionManager; | ||
148 | + | ||
138 | private final RelationService relationService; | 149 | private final RelationService relationService; |
139 | private final DeviceCredentialsService deviceCredentialsService; | 150 | private final DeviceCredentialsService deviceCredentialsService; |
140 | private final DbCallbackExecutorService dbCallbackExecutorService; | 151 | private final DbCallbackExecutorService dbCallbackExecutorService; |
@@ -192,6 +203,9 @@ public class DefaultTransportApiService implements TransportApiService { | @@ -192,6 +203,9 @@ public class DefaultTransportApiService implements TransportApiService { | ||
192 | else if (transportApiRequestMsg.hasScript()) { | 203 | else if (transportApiRequestMsg.hasScript()) { |
193 | result = handle(transportApiRequestMsg.getScript()); | 204 | result = handle(transportApiRequestMsg.getScript()); |
194 | } | 205 | } |
206 | + else if (transportApiRequestMsg.hasGbt28181RequestMsg()) { | ||
207 | + result = handleGbt(transportApiRequestMsg.getGbt28181RequestMsg()); | ||
208 | + } | ||
195 | 209 | ||
196 | 210 | ||
197 | return Futures.transform(Optional.ofNullable(result).orElseGet(this::getEmptyTransportApiResponseFuture), | 211 | return Futures.transform(Optional.ofNullable(result).orElseGet(this::getEmptyTransportApiResponseFuture), |
@@ -685,20 +699,74 @@ public class DefaultTransportApiService implements TransportApiService { | @@ -685,20 +699,74 @@ public class DefaultTransportApiService implements TransportApiService { | ||
685 | private ListenableFuture<TransportApiResponseMsg> handle(TransportProtos.ScriptProto requestMsg) { | 699 | private ListenableFuture<TransportApiResponseMsg> handle(TransportProtos.ScriptProto requestMsg) { |
686 | List<TkDeviceScriptDTO> allScriptes = scriptService.getScriptes(); | 700 | List<TkDeviceScriptDTO> allScriptes = scriptService.getScriptes(); |
687 | TransportApiResponseMsg.Builder responseBuilder = TransportApiResponseMsg.newBuilder(); | 701 | TransportApiResponseMsg.Builder responseBuilder = TransportApiResponseMsg.newBuilder(); |
688 | - allScriptes.forEach( | ||
689 | - item -> { | ||
690 | - UUID tenantId = UUID.fromString(item.getTenantId()); | ||
691 | - UUID id = UUID.fromString(item.getId()); | ||
692 | - responseBuilder.addScriptsResponseMsg( | ||
693 | - TransportProtos.ScriptProto.newBuilder() | ||
694 | - .setConvertJs(item.getConvertJs()) | ||
695 | - .setTenantIdLSB(tenantId.getLeastSignificantBits()) | ||
696 | - .setTenantIdMSB(tenantId.getMostSignificantBits()) | ||
697 | - .setScriptIdLSB(id.getLeastSignificantBits()) | ||
698 | - .setScriptIdMSB(id.getMostSignificantBits()) | ||
699 | - .setFunctionType(item.getScriptType().name()) | ||
700 | - .setStatus(item.getStatus())); | ||
701 | - }); | 702 | + allScriptes.forEach( |
703 | + item -> { | ||
704 | + UUID tenantId = UUID.fromString(item.getTenantId()); | ||
705 | + UUID id = UUID.fromString(item.getId()); | ||
706 | + responseBuilder.addScriptsResponseMsg( | ||
707 | + TransportProtos.ScriptProto.newBuilder() | ||
708 | + .setConvertJs(item.getConvertJs()) | ||
709 | + .setTenantIdLSB(tenantId.getLeastSignificantBits()) | ||
710 | + .setTenantIdMSB(tenantId.getMostSignificantBits()) | ||
711 | + .setScriptIdLSB(id.getLeastSignificantBits()) | ||
712 | + .setScriptIdMSB(id.getMostSignificantBits()) | ||
713 | + .setFunctionType(item.getScriptType().name()) | ||
714 | + .setStatus(item.getStatus())); | ||
715 | + }); | ||
702 | return Futures.immediateFuture(responseBuilder.build()); | 716 | return Futures.immediateFuture(responseBuilder.build()); |
703 | } | 717 | } |
718 | + private ListenableFuture<TransportApiResponseMsg> handleGbt(TransportProtos.Gbt28181RequestMsg requestMsg) { | ||
719 | + TransportProtos.Gbt28181ResponseMsg.Builder responseMsgBuilder =TransportProtos.Gbt28181ResponseMsg.newBuilder(); | ||
720 | + String tenantId = new UUID(requestMsg.getTenantIdMSB(), requestMsg.getTenantIdLSB()).toString(); | ||
721 | + String tbDeviceId = new UUID(requestMsg.getEntityIdMSB(), requestMsg.getEntityIdLSB()).toString(); | ||
722 | + String type = requestMsg.getContextType(); | ||
723 | + switch (VideoCmdEnum.valueOf(type)){ | ||
724 | + case DeviceInfo: | ||
725 | + Optional<SipDeviceDTO> camera = dataDecodingEncodingService.decode(requestMsg.getContext().toByteArray()); | ||
726 | + camera.ifPresent(sip ->{ | ||
727 | + ytDeviceService.updateDeviceInfo(tenantId,tbDeviceId, FastIotConstants.DeviceAdditional.SIP,JacksonUtil.valueToTree(sip)); | ||
728 | + }); | ||
729 | + | ||
730 | + break; | ||
731 | + case Catalog: | ||
732 | + Optional<List<VideoChanelDTO>> chanel = dataDecodingEncodingService.decode(requestMsg.getContext().toByteArray()); | ||
733 | + chanel.ifPresent(d->{ | ||
734 | + d.forEach(channelService::saveOrUpdate); | ||
735 | + }); | ||
736 | + break; | ||
737 | + default: | ||
738 | + release(tenantId,tbDeviceId); | ||
739 | + | ||
740 | + } | ||
741 | +// byte[] channaelMsgBytes = dataDecodingEncodingService.encode(channelDTO); | ||
742 | +// responseMsgBuilder.setContext(ByteString.copyFrom(channaelMsgBytes)); | ||
743 | + return Futures.immediateFuture(TransportApiResponseMsg.newBuilder().setGbt28181ResponseMsg(responseMsgBuilder.build()).build()); | ||
744 | + } | ||
745 | + private void release(String tenantId,String tbDeviceId){ | ||
746 | + DeviceDTO device = ytDeviceService.findDeviceInfoByTbDeviceId(tenantId,tbDeviceId); | ||
747 | + Optional.ofNullable(device).ifPresent(dev->{ | ||
748 | + JsonNode sip = dev.getDeviceInfo().get(FastIotConstants.DeviceAdditional.SIP); | ||
749 | + if(!sip.isEmpty() && sip.has(FastIotConstants.ZLMediaBody.CAMERA_CODE)){ | ||
750 | + String cameraCode = sip.get(FastIotConstants.ZLMediaBody.CAMERA_CODE).asText(); | ||
751 | + Optional<Set<String>> ssrcKeys = | ||
752 | + videoStreamSessionManager.getSsrcTransactionCamaraKey(cameraCode); | ||
753 | + ssrcKeys.ifPresent(all ->{ | ||
754 | + all.forEach(fullKey->{ | ||
755 | + videoStreamSessionManager.getSsrcTransaction(fullKey).ifPresent(ssrcTransaction -> { | ||
756 | + mediaServerNodeService.releaseSsrc( | ||
757 | + ssrcTransaction.getMediaServerId(), ssrcTransaction.getSsrc()); | ||
758 | + mediaServerNodeService.closeRTPServer( | ||
759 | + ssrcTransaction.getMediaServerId(), ssrcTransaction.getStream()); | ||
760 | + videoStreamSessionManager.remove( | ||
761 | + cameraCode, ssrcTransaction.getChannelId(), | ||
762 | + ssrcTransaction.getStream()); | ||
763 | + }); | ||
764 | + | ||
765 | + }); | ||
766 | + }); | ||
767 | + } | ||
768 | + }); | ||
769 | + | ||
770 | + | ||
771 | + } | ||
704 | } | 772 | } |
application/src/main/java/org/thingsboard/server/service/yunteng/media/TkVideoControlService.java
0 → 100644
1 | +package org.thingsboard.server.service.yunteng.media; | ||
2 | + | ||
3 | +import com.fasterxml.jackson.databind.JsonNode; | ||
4 | +import org.apache.curator.shaded.com.google.common.util.concurrent.ListenableFuture; | ||
5 | +import org.thingsboard.server.common.data.exception.ThingsboardException; | ||
6 | +import org.thingsboard.server.common.data.yunteng.dto.sip.*; | ||
7 | +import org.thingsboard.server.common.data.yunteng.enums.PTZCommandEnum; | ||
8 | +import org.thingsboard.server.common.msg.rpc.FromDeviceRpcResponse; | ||
9 | +import org.thingsboard.server.service.security.model.SecurityUser; | ||
10 | + | ||
11 | +import java.util.function.Consumer; | ||
12 | + | ||
13 | +public interface TkVideoControlService { | ||
14 | + | ||
15 | + /** | ||
16 | + * 视频点播 | ||
17 | + * | ||
18 | + * @param deviceId 设备ID | ||
19 | + * @param channelId 通道ID | ||
20 | + * @param tenantId 租户ID | ||
21 | + * @return 视频流播放地址内容 | ||
22 | + */ | ||
23 | + ListenableFuture<StreamContentDTO> startPlay( | ||
24 | + SecurityUser currentUser, String deviceId, String channelId, String tenantId); | ||
25 | + | ||
26 | + /** | ||
27 | + * 当点播时的推送处理程序 | ||
28 | + * | ||
29 | + * @param mediaServerItem 流媒体信息 | ||
30 | + * @param response 响应 | ||
31 | + * @param deviceId 点播设备ID | ||
32 | + * @param channelId 点播设备通道ID | ||
33 | + */ | ||
34 | + StreamInfoDTO onPublishHandlerForPlay( | ||
35 | + MediaServerDTO mediaServerItem, JsonNode response, String deviceId, String channelId); | ||
36 | + | ||
37 | + /** | ||
38 | + * 通过设备ID和通道ID暂停播放 | ||
39 | + * | ||
40 | + * @param tbDeviceId TB设备的ID | ||
41 | + * @param channelId 通道ID | ||
42 | + * @return 暂停播放结果 true成功 false失败 | ||
43 | + */ | ||
44 | + boolean stopPlay(SecurityUser currentUser, String tbDeviceId, String channelId) throws ThingsboardException; | ||
45 | + | ||
46 | + /** | ||
47 | + * 摄像头控制 | ||
48 | + * @param currentUser 登录用户 | ||
49 | + * @param tbDeviceId 设备ID | ||
50 | + * @param channelId 设备通道 | ||
51 | + * @param command 控制指令 | ||
52 | + * @param horizonSpeed 水平速度 | ||
53 | + * @param verticalSpeed 垂直速度 | ||
54 | + * @param zoomSpeed 缩放速度 | ||
55 | + * @return | ||
56 | + */ | ||
57 | + boolean control(SecurityUser currentUser, String tbDeviceId, String channelId, PTZCommandEnum command, int horizonSpeed, int verticalSpeed, int zoomSpeed) throws ThingsboardException; | ||
58 | + | ||
59 | + StreamInfoDTO play( | ||
60 | + SecurityUser currentUser, | ||
61 | + String tbDeviceId, | ||
62 | + MediaServerDTO mediaServerItem, | ||
63 | + SsrcInfoDTO ssrcInfo, | ||
64 | + SipDeviceDTO device, | ||
65 | + String channelId); | ||
66 | + public void byeCmdInSsrcTransaction( | ||
67 | + String tenantId, | ||
68 | + boolean oneWay, | ||
69 | + String tbDeviceId, | ||
70 | + String cameraCode,String channelId,String streamId, | ||
71 | + Consumer<FromDeviceRpcResponse> responseConsumer) | ||
72 | + throws ThingsboardException; | ||
73 | +} |
1 | +package org.thingsboard.server.service.yunteng.media; | ||
2 | + | ||
3 | +import com.fasterxml.jackson.databind.JsonNode; | ||
4 | +import com.fasterxml.jackson.databind.node.ObjectNode; | ||
5 | +import java.util.Optional; | ||
6 | +import java.util.UUID; | ||
7 | +import java.util.Vector; | ||
8 | +import java.util.concurrent.CountDownLatch; | ||
9 | +import java.util.concurrent.TimeUnit; | ||
10 | +import java.util.concurrent.atomic.AtomicReference; | ||
11 | +import java.util.function.Consumer; | ||
12 | +import javax.sdp.*; | ||
13 | +import lombok.RequiredArgsConstructor; | ||
14 | +import lombok.extern.slf4j.Slf4j; | ||
15 | +import org.apache.curator.shaded.com.google.common.util.concurrent.Futures; | ||
16 | +import org.apache.curator.shaded.com.google.common.util.concurrent.ListenableFuture; | ||
17 | +import org.springframework.beans.factory.annotation.Value; | ||
18 | +import org.springframework.stereotype.Service; | ||
19 | +import org.thingsboard.server.common.data.StringUtils; | ||
20 | +import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; | ||
21 | +import org.thingsboard.server.common.data.exception.ThingsboardException; | ||
22 | +import org.thingsboard.server.common.data.id.DeviceId; | ||
23 | +import org.thingsboard.server.common.data.id.TenantId; | ||
24 | +import org.thingsboard.server.common.data.rpc.ToDeviceRpcRequestBody; | ||
25 | +import org.thingsboard.server.common.data.yunteng.common.media.VideoStreamSessionManager; | ||
26 | +import org.thingsboard.server.common.data.yunteng.common.media.ZlmHttpHookSubscribe; | ||
27 | +import org.thingsboard.server.common.data.yunteng.config.media.UserSetting; | ||
28 | +import org.thingsboard.server.common.data.yunteng.constant.FastIotConstants; | ||
29 | +import org.thingsboard.server.common.data.yunteng.core.exception.TkDataValidationException; | ||
30 | +import org.thingsboard.server.common.data.yunteng.core.message.ErrorMessage; | ||
31 | +import org.thingsboard.server.common.data.yunteng.dto.DeviceDTO; | ||
32 | +import org.thingsboard.server.common.data.yunteng.dto.sip.*; | ||
33 | +import org.thingsboard.server.common.data.yunteng.dto.sip.hook.param.HookSubscribeForStreamChange; | ||
34 | +import org.thingsboard.server.common.data.yunteng.enums.PTZCommandEnum; | ||
35 | +import org.thingsboard.server.common.data.yunteng.enums.SessionTypeEnum; | ||
36 | +import org.thingsboard.server.common.data.yunteng.enums.VideoMethodEnum; | ||
37 | +import org.thingsboard.server.common.data.yunteng.utils.JacksonUtil; | ||
38 | +import org.thingsboard.server.common.data.yunteng.utils.ZLMediaKitRestFulUtils; | ||
39 | +import org.thingsboard.server.common.msg.rpc.FromDeviceRpcResponse; | ||
40 | +import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest; | ||
41 | +import org.thingsboard.server.dao.util.yunteng.ZLMediaKitTaskUtils; | ||
42 | +import org.thingsboard.server.dao.yunteng.service.TkDeviceService; | ||
43 | +import org.thingsboard.server.dao.yunteng.service.media.*; | ||
44 | +import org.thingsboard.server.service.rpc.TbCoreDeviceRpcService; | ||
45 | +import org.thingsboard.server.service.security.model.SecurityUser; | ||
46 | + | ||
47 | +@Service | ||
48 | +@RequiredArgsConstructor | ||
49 | +@Slf4j | ||
50 | +public class TkVideoControlServiceImpl implements TkVideoControlService { | ||
51 | + | ||
52 | + private final TkMediaServerService tkMediaServerService; | ||
53 | + private final TkMediaServerNodeService tkMediaServerNodeService; | ||
54 | + private final TkDeviceService tkDeviceService; | ||
55 | + private final TkVideoChannelService tkVideoChannelService; | ||
56 | + private final TkCacheStorageService tkCacheStorageService; | ||
57 | + private final ZLMediaKitTaskUtils zlMediaKitTaskUtils; | ||
58 | + private final UserSetting userSetting; | ||
59 | + private final ZlmHttpHookSubscribe subscribe; | ||
60 | + private final VideoStreamSessionManager videoStreamSessionManager; | ||
61 | + private final ZLMediaKitRestFulUtils zlMediaKitRestFulUtils; | ||
62 | + private final TbCoreDeviceRpcService deviceRpcService; | ||
63 | + | ||
64 | + @Override | ||
65 | + public ListenableFuture<StreamContentDTO> startPlay( | ||
66 | + SecurityUser currentUser, String deviceId, String channelId, String tenantId) { | ||
67 | + // 判断设备、通道是否存在 | ||
68 | + DeviceDTO deviceDTO = tkDeviceService.checkDeviceByTenantIdAndId(tenantId, deviceId, true); | ||
69 | + SipDeviceDTO sipDeviceDTO = | ||
70 | + JacksonUtil.convertValue( | ||
71 | + deviceDTO.getDeviceInfo().get(FastIotConstants.DeviceAdditional.SIP), | ||
72 | + SipDeviceDTO.class); | ||
73 | + // 获取设备的附加信息 | ||
74 | + if (null == sipDeviceDTO) { | ||
75 | + throw new TkDataValidationException(ErrorMessage.FOUND_VIDEO_DEVICE_FAILED.getMessage()); | ||
76 | + } | ||
77 | + VideoChanelDTO videoChanelDTO = tkVideoChannelService.findVideoChannelById(sipDeviceDTO.getCameraCode(), channelId, tenantId); | ||
78 | + if (null == videoChanelDTO) { | ||
79 | + throw new TkDataValidationException(ErrorMessage.VIDEO_CHANNEL_NOT_FOUND.getMessage()); | ||
80 | + } | ||
81 | + // 找到可以使用的流媒体 | ||
82 | + MediaServerDTO mediaServer = | ||
83 | + tkMediaServerService.getMediaServerForPlay(tenantId, sipDeviceDTO.getMediaServerId()); | ||
84 | + if (null == mediaServer) { | ||
85 | + throw new TkDataValidationException( | ||
86 | + ErrorMessage.NOT_FOUND_MEDIA_SERVER_FOR_PLAY.getMessage()); | ||
87 | + } | ||
88 | + // 根据设备ID和通道ID,找到对应的流信息 | ||
89 | + Optional<StreamInfoDTO> result = | ||
90 | + tkCacheStorageService.queryPlayStreamByChannel(sipDeviceDTO.getCameraCode(), channelId); | ||
91 | + if (result.isPresent()) { | ||
92 | + StreamInfoDTO streamInfoDTO = result.get(); | ||
93 | + String streamId = streamInfoDTO.getStream(); | ||
94 | + if (StringUtils.isNotEmpty(streamId)) { | ||
95 | + String mediaServerId = streamInfoDTO.getMediaServerId(); | ||
96 | + Optional<MediaServerDTO> mediaServerDTO = tkMediaServerNodeService.getOne(mediaServerId); | ||
97 | + if (mediaServerDTO.isPresent()) { | ||
98 | + if (checkRTPServerExist(mediaServerDTO.get(), streamInfoDTO, tenantId)) { | ||
99 | + log.debug(String.format("缓存的【流媒体播放信息】内容【%s】", streamInfoDTO)); | ||
100 | + return Futures.immediateFuture(new StreamContentDTO(streamInfoDTO)); | ||
101 | + } | ||
102 | + } | ||
103 | + } | ||
104 | + } | ||
105 | + | ||
106 | + StreamInfoDTO streamInfoDTO = | ||
107 | + deviceFirstPlay( | ||
108 | + currentUser, deviceDTO.getTbDeviceId(), mediaServer, sipDeviceDTO, videoChanelDTO); | ||
109 | + if (Optional.ofNullable(streamInfoDTO).isPresent()) { | ||
110 | + return Futures.immediateFuture(new StreamContentDTO(streamInfoDTO)); | ||
111 | + } else { | ||
112 | + return Futures.immediateFuture(new StreamContentDTO(null)); | ||
113 | + } | ||
114 | + } | ||
115 | + | ||
116 | + @Override | ||
117 | + public StreamInfoDTO onPublishHandlerForPlay( | ||
118 | + MediaServerDTO mediaServerItem, JsonNode response, String deviceCode, String channelId) { | ||
119 | + StreamInfoDTO streamInfo = onPublishHandler(mediaServerItem, response, deviceCode, channelId); | ||
120 | + VideoChanelDTO videoChanel = | ||
121 | + tkVideoChannelService.findVideoChannelById( | ||
122 | + deviceCode, channelId, mediaServerItem.getTenantId()); | ||
123 | + if (videoChanel != null) { | ||
124 | + videoChanel.setStreamId(streamInfo.getStream()); | ||
125 | + tkVideoChannelService.updateVideoChannelStreamId( | ||
126 | + streamInfo.getStream(), deviceCode, channelId); | ||
127 | + } | ||
128 | + tkCacheStorageService.cacheStreamInfoByStartPlay(streamInfo); | ||
129 | + return streamInfo; | ||
130 | + } | ||
131 | + | ||
132 | + @Override | ||
133 | + public boolean stopPlay(SecurityUser currentUser, String tbDeviceId, String channelId) throws ThingsboardException { | ||
134 | + String tenantId = currentUser.getCurrentTenantId(); | ||
135 | + DeviceDTO deviceDTO = tkDeviceService.findDeviceInfoByTbDeviceId(tenantId, tbDeviceId); | ||
136 | + if (null == deviceDTO | ||
137 | + || !deviceDTO.getDeviceInfo().has(FastIotConstants.DeviceAdditional.SIP)) { | ||
138 | + throw new TkDataValidationException(ErrorMessage.INVALID_PARAMETER.getMessage()); | ||
139 | + } | ||
140 | + String cameraCode = | ||
141 | + deviceDTO | ||
142 | + .getDeviceInfo() | ||
143 | + .get(FastIotConstants.DeviceAdditional.SIP) | ||
144 | + .get(FastIotConstants.ZLMediaBody.CAMERA_CODE) | ||
145 | + .asText(); | ||
146 | + | ||
147 | + Optional<StreamInfoDTO> streamInfoDTO = | ||
148 | + tkCacheStorageService.queryPlayStreamByChannel(cameraCode, channelId); | ||
149 | + if (streamInfoDTO.isEmpty()) { | ||
150 | + throw new TkDataValidationException( | ||
151 | + ErrorMessage.STREAM_INFO_NOT_FOUND_FOR_PLAY.getMessage()); | ||
152 | + } | ||
153 | + byeCmdInSsrcTransaction( | ||
154 | + currentUser.getCurrentTenantId(), | ||
155 | + false, | ||
156 | + tbDeviceId, | ||
157 | + cameraCode,channelId,streamInfoDTO.get().getStream(), | ||
158 | + fromDeviceRpcResponse->{ | ||
159 | + | ||
160 | + }); | ||
161 | + | ||
162 | + tkCacheStorageService.deleteCacheStreamInfoByStopPlay(streamInfoDTO.get()); //redisCatchStorage.stopPlay(streamInfo); | ||
163 | + return tkVideoChannelService.updateVideoChannelStreamId(null, cameraCode, channelId);//storager.stopPlay(streamInfo.getDeviceID(), streamInfo.getChannelId()); | ||
164 | + } | ||
165 | + | ||
166 | + @Override | ||
167 | + public StreamInfoDTO play( | ||
168 | + SecurityUser currentUser, | ||
169 | + String tbDeviceId, | ||
170 | + MediaServerDTO mediaServerDTO, | ||
171 | + SsrcInfoDTO ssrcInfoDTO, | ||
172 | + SipDeviceDTO sipDeviceDTO, | ||
173 | + String channelId) { | ||
174 | + HookSubscribeForStreamChange hookSubscribe = | ||
175 | + new HookSubscribeForStreamChange( | ||
176 | + "rtp", ssrcInfoDTO.getStream(), true, "rtsp", mediaServerDTO.getMediaServerId()); | ||
177 | + CountDownLatch timeoutLatch = new CountDownLatch(1); | ||
178 | + // 定时任务超时KEY | ||
179 | + String timeOutTaskKey = UUID.randomUUID().toString(); | ||
180 | + zlMediaKitTaskUtils.startCronTaskDelay( | ||
181 | + timeOutTaskKey, | ||
182 | + () -> { | ||
183 | + log.debug( | ||
184 | + "[点播超时] 收流超时 deviceId: {}, channelId: {},端口:{}, SSRC: {}", | ||
185 | + sipDeviceDTO.getCameraCode(), | ||
186 | + channelId, | ||
187 | + ssrcInfoDTO.getPort(), | ||
188 | + ssrcInfoDTO.getSsrc()); | ||
189 | + // 点播超时回复BYE 同时释放ssrc以及此次点播的资源 | ||
190 | + try { | ||
191 | + // cmder.streamByeCmd(device, channelId, ssrcInfo.getStream(), null); | ||
192 | + // TODO 回复Bye命令 | ||
193 | + } catch (Exception e) { | ||
194 | + log.error("[点播超时], 发送BYE失败 {}", e.getMessage()); | ||
195 | + } finally { | ||
196 | + // timeoutCallback.run(1, "收流超时"); | ||
197 | + // 释放SSRC | ||
198 | + tkMediaServerNodeService.releaseSsrc( | ||
199 | + mediaServerDTO.getMediaServerId(), ssrcInfoDTO.getSsrc()); | ||
200 | + // streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream()); | ||
201 | + // 关闭RTP服务 | ||
202 | + tkMediaServerNodeService.closeRTPServer( | ||
203 | + Optional.of(mediaServerDTO), ssrcInfoDTO.getStream()); | ||
204 | + // 取消订阅消息监听 | ||
205 | + subscribe.removeSubscribe(hookSubscribe); | ||
206 | + } | ||
207 | + }, | ||
208 | + userSetting.getPlayTimeout()); | ||
209 | + // 获取端口信息失败,则不发送点播指令 | ||
210 | + if (ssrcInfoDTO.getPort() <= FastIotConstants.MagicNumber.ZERO) { | ||
211 | + zlMediaKitTaskUtils.stop(timeOutTaskKey); | ||
212 | + // 释放SSRC | ||
213 | + tkMediaServerNodeService.releaseSsrc( | ||
214 | + mediaServerDTO.getMediaServerId(), ssrcInfoDTO.getSsrc()); | ||
215 | + videoStreamSessionManager.remove( | ||
216 | + sipDeviceDTO.getCameraCode(), channelId, ssrcInfoDTO.getStream()); | ||
217 | + throw new TkDataValidationException(ErrorMessage.GET_PLAY_PORT_FAILED.getMessage()); | ||
218 | + } | ||
219 | + // 进行命令发送 | ||
220 | + AtomicReference<StreamInfoDTO> result = new AtomicReference<>(); | ||
221 | + try { | ||
222 | + | ||
223 | + log.debug( | ||
224 | + String.format( | ||
225 | + "视频流【%s】的流媒体服务器信息=流媒体服务ID【%s】+流媒体服务端口【%s】+SDP(会话)IP【%s】", | ||
226 | + ssrcInfoDTO.getStream(), | ||
227 | + mediaServerDTO.getMediaServerId(), | ||
228 | + ssrcInfoDTO.getPort(), | ||
229 | + mediaServerDTO.getSdpIp())); | ||
230 | + ObjectNode paramJson = JacksonUtil.newObjectNode(); | ||
231 | + paramJson.set(FastIotConstants.ZLMediaBody.MEDIA, JacksonUtil.valueToTree(mediaServerDTO)); | ||
232 | + // | ||
233 | + // paramJson.put(FastIotConstants.ZLMediaBody.MEDIA_ID,mediaServerDTO.getMediaServerId()); | ||
234 | + // paramJson.put(FastIotConstants.ZLMediaBody.MEDIA_SDP_IP,mediaServerDTO.getSdpIp()); | ||
235 | + paramJson.put(FastIotConstants.ZLMediaBody.SSRCINFO_STREAM, ssrcInfoDTO.getStream()); | ||
236 | + paramJson.put(FastIotConstants.ZLMediaBody.SSRCINFO_PORT, ssrcInfoDTO.getPort()); | ||
237 | + paramJson.put(FastIotConstants.ZLMediaBody.SSRCINFO_SSRC, ssrcInfoDTO.getSsrc()); | ||
238 | + paramJson.put(FastIotConstants.ZLMediaBody.CHANNEL_ID, channelId); | ||
239 | + // TODO: 2023/12/28 收到流改变事件后,视频点播的业务逻辑。 | ||
240 | + subscribe.addSubscribe( | ||
241 | + hookSubscribe, | ||
242 | + (MediaServerDTO mediaServerItemInUse, JsonNode response) -> { | ||
243 | + log.debug( | ||
244 | + "处理订阅消息:主题【{}】流媒体信息【{}】收到的内容【{}】", | ||
245 | + hookSubscribe.getHookType().name(), | ||
246 | + mediaServerItemInUse, | ||
247 | + response); | ||
248 | + zlMediaKitTaskUtils.stop(timeOutTaskKey); | ||
249 | + | ||
250 | + subscribe.removeSubscribe(hookSubscribe); | ||
251 | + result.set( | ||
252 | + steamImage( | ||
253 | + ssrcInfoDTO.getStream(), | ||
254 | + sipDeviceDTO.getCameraCode(), | ||
255 | + channelId, | ||
256 | + mediaServerItemInUse, | ||
257 | + response)); | ||
258 | + timeoutLatch.countDown(); | ||
259 | + }); | ||
260 | + cameraCommonCmd( | ||
261 | + currentUser.getCurrentTenantId(), | ||
262 | + paramJson, | ||
263 | + sipDeviceDTO.getCameraCode(), | ||
264 | + VideoMethodEnum.INVITE, | ||
265 | + false, | ||
266 | + tbDeviceId, | ||
267 | + fromDeviceRpcResponse -> { | ||
268 | + log.warn("【流媒体SIP】收到【视频点播】结果=异常【{}】+数据【{}】",fromDeviceRpcResponse.getError(), fromDeviceRpcResponse.getResponse()); | ||
269 | + fromDeviceRpcResponse | ||
270 | + .getResponse() | ||
271 | + .ifPresent( | ||
272 | + jsonStr -> { | ||
273 | + JsonNode responseJson = JacksonUtil.toJsonNode(jsonStr); | ||
274 | + if (fromDeviceRpcResponse.getError().isEmpty()) { | ||
275 | + playSuccess( | ||
276 | + currentUser, | ||
277 | + tbDeviceId, | ||
278 | + mediaServerDTO, | ||
279 | + responseJson, | ||
280 | + ssrcInfoDTO, | ||
281 | + sipDeviceDTO, | ||
282 | + channelId, | ||
283 | + timeOutTaskKey); | ||
284 | + } else { | ||
285 | + zlMediaKitTaskUtils.stop(timeOutTaskKey); | ||
286 | + tkMediaServerNodeService.closeRTPServer( | ||
287 | + mediaServerDTO, ssrcInfoDTO.getStream(), null); | ||
288 | + // 释放ssrc | ||
289 | + tkMediaServerNodeService.releaseSsrc( | ||
290 | + mediaServerDTO.getMediaServerId(), ssrcInfoDTO.getSsrc()); | ||
291 | + videoStreamSessionManager.remove( | ||
292 | + sipDeviceDTO.getCameraCode(), channelId, ssrcInfoDTO.getStream()); | ||
293 | + // errorEvent.response(event); | ||
294 | + } | ||
295 | + }); | ||
296 | + }); | ||
297 | + timeoutLatch.await(userSetting.getPlayTimeout(), TimeUnit.MILLISECONDS); | ||
298 | + } catch (Exception e) { | ||
299 | + zlMediaKitTaskUtils.stop(timeOutTaskKey); | ||
300 | + tkMediaServerNodeService.closeRTPServer( | ||
301 | + Optional.ofNullable(mediaServerDTO), ssrcInfoDTO.getStream()); | ||
302 | + // 释放ssrc | ||
303 | + tkMediaServerNodeService.releaseSsrc(mediaServerDTO.getId(), ssrcInfoDTO.getSsrc()); | ||
304 | + | ||
305 | + videoStreamSessionManager.remove( | ||
306 | + sipDeviceDTO.getCameraCode(), channelId, ssrcInfoDTO.getStream()); | ||
307 | + | ||
308 | + // SipSubscribe.EventResult eventResult = new SipSubscribe.EventResult(); | ||
309 | + // eventResult.type = SipSubscribe.EventResultType.cmdSendFailEvent; | ||
310 | + // eventResult.statusCode = -1; | ||
311 | + // eventResult.msg = "命令发送失败"; | ||
312 | + // errorEvent.response(eventResult); | ||
313 | + } | ||
314 | + log.debug(String.format("最新的【流媒体播放信息】内容【%s】", result.get())); | ||
315 | + return result.get(); | ||
316 | + } | ||
317 | + | ||
318 | + /** | ||
319 | + * 调用流媒体接口获取视频流封面图,并触发后续业务 | ||
320 | + * | ||
321 | + * @param ssrcStream | ||
322 | + * @param cameraCode | ||
323 | + * @param channelId | ||
324 | + * @param mediaServerItemInUse | ||
325 | + * @param response | ||
326 | + */ | ||
327 | + private StreamInfoDTO steamImage( | ||
328 | + String ssrcStream, | ||
329 | + String cameraCode, | ||
330 | + String channelId, | ||
331 | + MediaServerDTO mediaServerItemInUse, | ||
332 | + JsonNode response) { | ||
333 | + // hook响应 | ||
334 | + StreamInfoDTO streamInfoDTO = | ||
335 | + onPublishHandlerForPlay(mediaServerItemInUse, response, cameraCode, channelId); | ||
336 | + String streamUrl; | ||
337 | + if (mediaServerItemInUse.getRtspPort() != 0) { | ||
338 | + streamUrl = | ||
339 | + String.format( | ||
340 | + "rtsp://127.0.0.1:%s/%s/%s", mediaServerItemInUse.getRtspPort(), "rtp", ssrcStream); | ||
341 | + } else { | ||
342 | + streamUrl = | ||
343 | + String.format( | ||
344 | + "http://127.0.0.1:%s/%s/%s.live.mp4", | ||
345 | + mediaServerItemInUse.getHttpPort(), "rtp", ssrcStream); | ||
346 | + } | ||
347 | + String path = "snap"; | ||
348 | + String fileName = cameraCode + "_" + channelId + ".jpg"; | ||
349 | + // 请求截图 | ||
350 | + log.debug("[请求截图]: " + fileName); | ||
351 | + zlMediaKitRestFulUtils.getSnap(mediaServerItemInUse, streamUrl, 15, 1, path, fileName); | ||
352 | + return streamInfoDTO; | ||
353 | + } | ||
354 | + | ||
355 | + /** | ||
356 | + * 构建视频点播的流媒体信息 | ||
357 | + * | ||
358 | + * @param mediaServerItem | ||
359 | + * @param response | ||
360 | + * @param deviceCode | ||
361 | + * @param channelId | ||
362 | + * @return | ||
363 | + */ | ||
364 | + private StreamInfoDTO onPublishHandler( | ||
365 | + MediaServerDTO mediaServerItem, JsonNode response, String deviceCode, String channelId) { | ||
366 | + String streamId = response.get("stream").asText(); | ||
367 | + JsonNode tracks = response.get("tracks"); | ||
368 | + StreamInfoDTO streamInfo = | ||
369 | + tkMediaServerService.getStreamInfoByAppAndStream( | ||
370 | + mediaServerItem, "rtp", streamId, tracks, null); | ||
371 | + streamInfo.setCameraCode(deviceCode); | ||
372 | + streamInfo.setChannelId(channelId); | ||
373 | + return streamInfo; | ||
374 | + } | ||
375 | + | ||
376 | + private StreamInfoDTO deviceFirstPlay( | ||
377 | + SecurityUser currentUser, | ||
378 | + String tbDeviceId, | ||
379 | + MediaServerDTO mediaServerDTO, | ||
380 | + SipDeviceDTO sipDeviceDTO, | ||
381 | + VideoChanelDTO videoChanelDTO) { | ||
382 | + // 进行点播时,需要的参数包含:schema、vhost、app、stream | ||
383 | + String streamId = null; | ||
384 | + // 是否开启了多端口模式 | ||
385 | + if (mediaServerDTO.isRtpEnable()) { | ||
386 | + streamId = | ||
387 | + String.format("%s_%s", sipDeviceDTO.getCameraCode(), videoChanelDTO.getChannelId()); | ||
388 | + } | ||
389 | + // 开启收流服务 | ||
390 | + SsrcInfoDTO ssrcInfoDTO = | ||
391 | + tkMediaServerNodeService.openRTPServer( | ||
392 | + mediaServerDTO, | ||
393 | + streamId, | ||
394 | + null, | ||
395 | + sipDeviceDTO.isSsrcCheck(), | ||
396 | + false, | ||
397 | + 0, | ||
398 | + false, | ||
399 | + sipDeviceDTO.getStreamModeForParam()); | ||
400 | + if (null == ssrcInfoDTO) { | ||
401 | + throw new TkDataValidationException(ErrorMessage.RECEIVE_STREAM_FAILED.getMessage()); | ||
402 | + } | ||
403 | + // 开始进行点播 | ||
404 | + return play( | ||
405 | + currentUser, | ||
406 | + tbDeviceId, | ||
407 | + mediaServerDTO, | ||
408 | + ssrcInfoDTO, | ||
409 | + sipDeviceDTO, | ||
410 | + videoChanelDTO.getChannelId()); | ||
411 | + } | ||
412 | + | ||
413 | + private boolean checkRTPServerExist( | ||
414 | + MediaServerDTO mediaServerDTO, StreamInfoDTO streamInfo, String tenantId) { | ||
415 | + String streamId = streamInfo.getStream(); | ||
416 | + JsonNode rtpInfo = zlMediaKitRestFulUtils.getRtpInfo(mediaServerDTO, streamId); | ||
417 | + if (rtpInfo.get("code").asInt() == 0 && rtpInfo.get("exist").asBoolean()) { | ||
418 | + int localPort = rtpInfo.get("local_port").asInt(); | ||
419 | + if (localPort == 0) { | ||
420 | + throw new TkDataValidationException("点播已经在进行中,请稍候重试"); | ||
421 | + } | ||
422 | + return true; | ||
423 | + } else { | ||
424 | + String deviceId = streamInfo.getCameraCode(); | ||
425 | + String channelId = streamInfo.getChannelId(); | ||
426 | + tkCacheStorageService.deleteCacheStreamInfoByStopPlay(streamInfo); | ||
427 | + tkVideoChannelService.updateVideoChannelStreamId(null, deviceId, channelId); | ||
428 | + return false; | ||
429 | + } | ||
430 | + } | ||
431 | + | ||
432 | + @Value("${server.rest.server_side_rpc.min_timeout:5000}") | ||
433 | + protected long minTimeout; | ||
434 | + | ||
435 | + @Value("${server.rest.server_side_rpc.default_timeout:10000}") | ||
436 | + protected long defaultTimeout; | ||
437 | + | ||
438 | + /** | ||
439 | + * 公用的命令下发接口 | ||
440 | + * | ||
441 | + * @param tenantId | ||
442 | + * @param paramJson | ||
443 | + * @param oneWay | ||
444 | + * @param deviceId TB的设备ID | ||
445 | + * @param responseConsumer | ||
446 | + * @throws ThingsboardException | ||
447 | + */ | ||
448 | + public void cameraCommonCmd( | ||
449 | + String tenantId, | ||
450 | + ObjectNode paramJson, | ||
451 | + String cameraCode, | ||
452 | + VideoMethodEnum method, | ||
453 | + boolean oneWay, | ||
454 | + String deviceId, | ||
455 | + Consumer<FromDeviceRpcResponse> responseConsumer) | ||
456 | + throws ThingsboardException { | ||
457 | + try { | ||
458 | + | ||
459 | + paramJson.put(FastIotConstants.ZLMediaBody.CAMERA_CODE, cameraCode); | ||
460 | + paramJson.put(FastIotConstants.ZLMediaBody.METHOD_TYPE, method.name()); | ||
461 | + ToDeviceRpcRequestBody body = | ||
462 | + new ToDeviceRpcRequestBody("cameraMethod", JacksonUtil.toString(paramJson)); | ||
463 | + long timeout = defaultTimeout; | ||
464 | + long expTime = System.currentTimeMillis() + Math.max(minTimeout, timeout); | ||
465 | + UUID rpcRequestUUID = UUID.randomUUID(); | ||
466 | + boolean persisted = false; | ||
467 | + ToDeviceRpcRequest rpcRequest = | ||
468 | + new ToDeviceRpcRequest( | ||
469 | + rpcRequestUUID, new TenantId(UUID.fromString(tenantId)), | ||
470 | + new DeviceId(UUID.fromString(deviceId)), oneWay, expTime, body, persisted, null, null); | ||
471 | + deviceRpcService.processRestApiRpcRequest(rpcRequest, responseConsumer, null); | ||
472 | + } catch (IllegalArgumentException ioe) { | ||
473 | + throw new ThingsboardException( | ||
474 | + "Invalid request body", ioe, ThingsboardErrorCode.BAD_REQUEST_PARAMS); | ||
475 | + } | ||
476 | + } | ||
477 | + | ||
478 | + /** | ||
479 | + * 点播关闭命令下发 | ||
480 | + * | ||
481 | + * @param oneWay | ||
482 | + * @param tbDeviceId TB的设备ID | ||
483 | + * @param responseConsumer | ||
484 | + * @throws ThingsboardException | ||
485 | + */ | ||
486 | + @Override | ||
487 | + public void byeCmdInSsrcTransaction( | ||
488 | + String tenantId, | ||
489 | + boolean oneWay, | ||
490 | + String tbDeviceId, | ||
491 | + String cameraCode,String channelId,String streamId, | ||
492 | + Consumer<FromDeviceRpcResponse> responseConsumer) | ||
493 | + throws ThingsboardException { | ||
494 | + Optional<SsrcTransactionDTO> transactionDTO = videoStreamSessionManager.getSsrcTransaction(cameraCode,channelId,streamId); | ||
495 | + if (transactionDTO.isEmpty()) { | ||
496 | + throw new TkDataValidationException( | ||
497 | + ErrorMessage.STREAM_INFO_NOT_FOUND_FOR_PLAY.getMessage()); | ||
498 | + } | ||
499 | + SsrcTransactionDTO ssrc = transactionDTO.get(); | ||
500 | + cameraByeCmd( | ||
501 | + tenantId,ssrc.getSipTransactionInfo(),oneWay,true,tbDeviceId, | ||
502 | + cameraCode,channelId,streamId,ssrc.getSsrc(),ssrc.getMediaServerId(), | ||
503 | + responseConsumer); | ||
504 | + } | ||
505 | + | ||
506 | + public void byeCmdInSendRtp( | ||
507 | + SecurityUser currentUser, | ||
508 | + boolean oneWay, | ||
509 | + String tbDeviceId, | ||
510 | + String cameraCode,String channelId,String streamId, | ||
511 | + Consumer<FromDeviceRpcResponse> responseConsumer) | ||
512 | + throws ThingsboardException { | ||
513 | + | ||
514 | +// SendRtpItemDTO sendRtpItem =null; | ||
515 | +// SipMessageHeaderDTO sipTransactionInfo = | ||
516 | +// SipMessageHeaderDTO.builder() | ||
517 | +// .toTag(sendRtpItem.getToTag()) | ||
518 | +// .fromTag(sendRtpItem.getFromTag()) | ||
519 | +// .callId(sendRtpItem.getCallId()) | ||
520 | +// .build(); | ||
521 | +// MediaServerDTO mediaServer = tkMediaServerService.getMediaServerByMediaServerId(sendRtpItem.getMediaServerId()); | ||
522 | +// cameraByeCmd( | ||
523 | +// currentUser,ssrc.getSipTransactionInfo(),oneWay,mediaServer != null,tbDeviceId, | ||
524 | +// cameraCode,sendRtpItem.getChannelId(),streamId,ssrc.getSsrc(),ssrc.getMediaServerId(), | ||
525 | +// responseConsumer); | ||
526 | + } | ||
527 | + | ||
528 | + | ||
529 | + public void cameraByeCmd( | ||
530 | + String currentUser,SipMessageHeaderDTO messageHeaderDTO, | ||
531 | + boolean oneWay,boolean mediaOnline, | ||
532 | + String tbDeviceId, | ||
533 | + String cameraCode,String channelId,String streamId,String ssrc,String mediaServerId, | ||
534 | + Consumer<FromDeviceRpcResponse> responseConsumer) throws ThingsboardException { | ||
535 | + if(mediaOnline){ | ||
536 | + tkMediaServerNodeService.releaseSsrc(mediaServerId,ssrc); | ||
537 | + tkMediaServerNodeService.closeRTPServer(mediaServerId,streamId); | ||
538 | + } | ||
539 | + videoStreamSessionManager.remove(cameraCode,channelId,streamId); | ||
540 | + ObjectNode paramJson = JacksonUtil.newObjectNode(); | ||
541 | + paramJson.put(FastIotConstants.ZLMediaBody.CHANNEL_ID, channelId); | ||
542 | + paramJson.set(FastIotConstants.ZLMediaBody.MSG_HEADER, JacksonUtil.valueToTree(messageHeaderDTO)); | ||
543 | + cameraCommonCmd( | ||
544 | + currentUser, | ||
545 | + paramJson, | ||
546 | + cameraCode, | ||
547 | + VideoMethodEnum.BYE, | ||
548 | + oneWay, | ||
549 | + tbDeviceId, | ||
550 | + responseConsumer); | ||
551 | + } | ||
552 | + | ||
553 | + /** | ||
554 | + * 点播成功后,流媒体的业务逻辑 | ||
555 | + * | ||
556 | + * @param currentUser | ||
557 | + * @param tbDeviceId | ||
558 | + * @param mediaServerDTO | ||
559 | + * @param responseJson | ||
560 | + * @param ssrcInfo | ||
561 | + * @param sipDeviceDTO | ||
562 | + * @param channelId | ||
563 | + * @param timeOutTaskKey | ||
564 | + */ | ||
565 | + private void playSuccess( | ||
566 | + SecurityUser currentUser, | ||
567 | + String tbDeviceId, | ||
568 | + MediaServerDTO mediaServerDTO, | ||
569 | + JsonNode responseJson, | ||
570 | + SsrcInfoDTO ssrcInfo, | ||
571 | + SipDeviceDTO sipDeviceDTO, | ||
572 | + String channelId, | ||
573 | + String timeOutTaskKey) { | ||
574 | + String cameraCode = sipDeviceDTO.getCameraCode(); | ||
575 | + String callId = responseJson.get(FastIotConstants.ZLMediaBody.CALL_ID).asText(); | ||
576 | + JsonNode msgHeader = responseJson.get(FastIotConstants.ZLMediaBody.MSG_HEADER); | ||
577 | + SipMessageHeaderDTO sipMessage = JacksonUtil.convertValue(msgHeader, SipMessageHeaderDTO.class); | ||
578 | + String sessionType = responseJson.get(FastIotConstants.ZLMediaBody.SESSION_TYPE).asText(); | ||
579 | + String streamId = ssrcInfo.getStream(); | ||
580 | + videoStreamSessionManager.put( | ||
581 | + cameraCode, | ||
582 | + channelId, | ||
583 | + callId, | ||
584 | + streamId, | ||
585 | + ssrcInfo.getSsrc(), | ||
586 | + sipMessage, | ||
587 | + mediaServerDTO.getMediaServerId(), | ||
588 | + SessionTypeEnum.valueOf(sessionType)); | ||
589 | + // 获取ssrc | ||
590 | + String contentString = responseJson.get(FastIotConstants.ZLMediaBody.MSG_CONTEXT).asText(); | ||
591 | + int ssrcIndex = contentString.indexOf("y="); | ||
592 | + // 检查是否有y字段 | ||
593 | + if (ssrcIndex >= 0) { | ||
594 | + // ssrc规定长度为10字节,不取余下长度以避免后续还有“f=”字段 TODO 后续对不规范的非10位ssrc兼容 | ||
595 | + String ssrcInResponse = contentString.substring(ssrcIndex + 2, ssrcIndex + 12).trim(); | ||
596 | + // 查询到ssrc不一致且开启了ssrc校验则需要针对处理 | ||
597 | + if (ssrcInfo.getSsrc().equals(ssrcInResponse)) { | ||
598 | + if (sipDeviceDTO.getStreamMode().equalsIgnoreCase("TCP-ACTIVE")) { | ||
599 | + String substring = contentString.substring(0, contentString.indexOf("y=")); | ||
600 | + try { | ||
601 | + SessionDescription sdp = SdpFactory.getInstance().createSessionDescription(substring); | ||
602 | + int port = -1; | ||
603 | + Vector mediaDescriptions = sdp.getMediaDescriptions(true); | ||
604 | + for (Object description : mediaDescriptions) { | ||
605 | + MediaDescription mediaDescription = (MediaDescription) description; | ||
606 | + Media media = mediaDescription.getMedia(); | ||
607 | + | ||
608 | + Vector mediaFormats = media.getMediaFormats(false); | ||
609 | + if (mediaFormats.contains("96")) { | ||
610 | + port = media.getMediaPort(); | ||
611 | + break; | ||
612 | + } | ||
613 | + } | ||
614 | + log.debug( | ||
615 | + "[点播-TCP主动连接对方] deviceId: {}, channelId: {}, 连接对方的地址:{}:{}, 收流模式:{}, SSRC: {}, SSRC校验:{}", | ||
616 | + sipDeviceDTO.getCameraCode(), | ||
617 | + channelId, | ||
618 | + sdp.getConnection().getAddress(), | ||
619 | + port, | ||
620 | + sipDeviceDTO.getStreamMode(), | ||
621 | + ssrcInfo.getSsrc(), | ||
622 | + sipDeviceDTO.isSsrcCheck()); | ||
623 | + JsonNode jsonNode = | ||
624 | + zlMediaKitRestFulUtils.connectRtpServer( | ||
625 | + mediaServerDTO, sdp.getConnection().getAddress(), port, streamId); | ||
626 | + log.debug("[点播-TCP主动连接对方] 结果: {}", jsonNode); | ||
627 | + } catch (SdpException e) { | ||
628 | + log.error( | ||
629 | + "[点播-TCP主动连接对方] deviceId: {}, channelId: {}, 解析200OK的SDP信息失败", | ||
630 | + sipDeviceDTO.getCameraCode(), | ||
631 | + channelId, | ||
632 | + e); | ||
633 | + } | ||
634 | + } | ||
635 | + return; | ||
636 | + } | ||
637 | + log.debug("[点播消息] 收到invite 200, 发现下级自定义了ssrc: {}", ssrcInResponse); | ||
638 | + if (!mediaServerDTO.isRtpEnable() || sipDeviceDTO.isSsrcCheck()) { | ||
639 | + log.debug("[点播消息] SSRC修正 {}->{}", ssrcInfo.getSsrc(), ssrcInResponse); | ||
640 | + | ||
641 | + // 释放不被使用的ssrc | ||
642 | + tkMediaServerNodeService.releaseSsrc(mediaServerDTO.getMediaServerId(), ssrcInfo.getSsrc()); | ||
643 | + // 单端口模式streamId也有变化,需要重新设置监听 | ||
644 | + if (!mediaServerDTO.isRtpEnable()) { | ||
645 | + // 添加订阅 | ||
646 | + HookSubscribeForStreamChange hookSubscribe = | ||
647 | + new HookSubscribeForStreamChange( | ||
648 | + "rtp", streamId, true, "rtsp", mediaServerDTO.getMediaServerId()); | ||
649 | + subscribe.removeSubscribe(hookSubscribe); | ||
650 | + hookSubscribe | ||
651 | + .getContent() | ||
652 | + .put("stream", String.format("%08x", Integer.parseInt(ssrcInResponse)).toUpperCase()); | ||
653 | + subscribe.addSubscribe( | ||
654 | + hookSubscribe, | ||
655 | + (MediaServerDTO mediaServerItemInUse, JsonNode response) -> { | ||
656 | + log.debug( | ||
657 | + "处理订阅消息:主题【{}】流媒体信息【{}】收到的内容【{}】", | ||
658 | + hookSubscribe.getHookType().name(), | ||
659 | + mediaServerItemInUse, | ||
660 | + response); | ||
661 | + zlMediaKitTaskUtils.stop(timeOutTaskKey); | ||
662 | + // hook响应 | ||
663 | + onPublishHandlerForPlay(mediaServerItemInUse, response, cameraCode, channelId); | ||
664 | + // hookEvent.response(mediaServerItemInUse, response); | ||
665 | + }); | ||
666 | + } | ||
667 | + | ||
668 | + // 关闭rtp server | ||
669 | + tkMediaServerNodeService.closeRTPServer( | ||
670 | + mediaServerDTO, | ||
671 | + streamId, | ||
672 | + result -> { | ||
673 | + if (result) { | ||
674 | + // 重新开启ssrc server | ||
675 | + tkMediaServerNodeService.openRTPServer( | ||
676 | + mediaServerDTO, | ||
677 | + streamId, | ||
678 | + ssrcInResponse, | ||
679 | + sipDeviceDTO.isSsrcCheck(), | ||
680 | + false, | ||
681 | + ssrcInfo.getPort(), | ||
682 | + true, | ||
683 | + sipDeviceDTO.getStreamModeForParam()); | ||
684 | + } else { | ||
685 | + try { | ||
686 | + log.warn("[停止点播] {}/{}", cameraCode, channelId); | ||
687 | + byeCmdInSsrcTransaction( | ||
688 | + currentUser.getCurrentTenantId(), | ||
689 | + false, | ||
690 | + tbDeviceId, | ||
691 | + cameraCode,channelId,streamId, | ||
692 | + fromDeviceRpcResponse->{ | ||
693 | + | ||
694 | + }); | ||
695 | + } catch (Exception e) { | ||
696 | + log.error("[命令发送失败] 停止点播, 发送BYE: {}", e.getMessage()); | ||
697 | + } | ||
698 | + zlMediaKitTaskUtils.stop(timeOutTaskKey); | ||
699 | + videoStreamSessionManager.remove(cameraCode, channelId, streamId); | ||
700 | + // event.msg = "下级自定义了ssrc,重新设置收流信息失败"; | ||
701 | + // event.statusCode = 500; | ||
702 | + // errorEvent.response(event); | ||
703 | + } | ||
704 | + }); | ||
705 | + } | ||
706 | + } | ||
707 | + } | ||
708 | + | ||
709 | + @Override | ||
710 | + public boolean control(SecurityUser currentUser, String tbDeviceId, String channelId, PTZCommandEnum command, int horizonSpeed, int verticalSpeed, int zoomSpeed) throws ThingsboardException { | ||
711 | + | ||
712 | + if (PTZCommandEnum.STOP.equals(command)) { | ||
713 | + horizonSpeed = 0; | ||
714 | + verticalSpeed = 0; | ||
715 | + zoomSpeed = 0; | ||
716 | + } | ||
717 | + DeviceDTO deviceDTO = tkDeviceService.checkDeviceByTenantIdAndId(currentUser.getCurrentTenantId(), tbDeviceId, true); | ||
718 | + if (null == deviceDTO) { | ||
719 | + throw new TkDataValidationException(ErrorMessage.INVALID_PARAMETER.getMessage()); | ||
720 | + } | ||
721 | + SipDeviceDTO sipDeviceDTO = | ||
722 | + JacksonUtil.convertValue( | ||
723 | + deviceDTO.getDeviceInfo().get(FastIotConstants.DeviceAdditional.SIP), | ||
724 | + SipDeviceDTO.class); | ||
725 | + if (null == sipDeviceDTO) { | ||
726 | + throw new TkDataValidationException(ErrorMessage.INVALID_PARAMETER.getMessage()); | ||
727 | + } | ||
728 | + PTZCmdDTO ptzCmdDTO = new PTZCmdDTO(); | ||
729 | + ptzCmdDTO.setCommand(command); | ||
730 | + ptzCmdDTO.setHorizonSpeed(horizonSpeed); | ||
731 | + ptzCmdDTO.setVerticalSpeed(verticalSpeed); | ||
732 | + ptzCmdDTO.setZoomSpeed(zoomSpeed); | ||
733 | + ObjectNode requestJson = JacksonUtil.newObjectNode(); | ||
734 | + requestJson.put(FastIotConstants.ZLMediaBody.CHANNEL_ID, channelId); | ||
735 | + requestJson.set(FastIotConstants.ZLMediaBody.CONTROL_CONTEXT, JacksonUtil.valueToTree(ptzCmdDTO)); | ||
736 | + cameraCommonCmd( | ||
737 | + currentUser.getCurrentTenantId(), | ||
738 | + requestJson, | ||
739 | + sipDeviceDTO.getCameraCode(), | ||
740 | + VideoMethodEnum.MESSAGE, | ||
741 | + false, | ||
742 | + tbDeviceId, | ||
743 | + fromDeviceRpcResponse->{ | ||
744 | + | ||
745 | + }); | ||
746 | + return false; | ||
747 | + } | ||
748 | +} |
application/src/main/java/org/thingsboard/server/service/yunteng/media/ZLMediaKitStateRunner.java
0 → 100644
1 | +package org.thingsboard.server.service.yunteng.media; | ||
2 | + | ||
3 | +import com.fasterxml.jackson.databind.JsonNode; | ||
4 | +import lombok.Getter; | ||
5 | +import lombok.extern.slf4j.Slf4j; | ||
6 | +import org.springframework.beans.factory.annotation.Value; | ||
7 | +import org.springframework.stereotype.Component; | ||
8 | +import org.thingsboard.server.common.data.id.EntityId; | ||
9 | +import org.thingsboard.server.common.data.yunteng.config.media.ZLMediaKitServerConfig; | ||
10 | +import org.thingsboard.server.common.data.yunteng.constant.FastIotConstants; | ||
11 | +import org.thingsboard.server.common.data.yunteng.dto.sip.MediaServerDTO; | ||
12 | +import org.thingsboard.server.common.data.yunteng.utils.JacksonUtil; | ||
13 | +import org.thingsboard.server.common.data.yunteng.utils.ZLMediaKitRestFulUtils; | ||
14 | +import org.thingsboard.server.dao.util.yunteng.ZLMediaKitTaskUtils; | ||
15 | +import org.thingsboard.server.dao.yunteng.service.media.TkMediaServerNodeService; | ||
16 | +import org.thingsboard.server.dao.yunteng.service.media.TkMediaServerService; | ||
17 | +import org.thingsboard.server.common.data.yunteng.config.media.MediaConfig; | ||
18 | +import org.thingsboard.server.queue.util.TbCoreComponent; | ||
19 | + | ||
20 | +import javax.annotation.PostConstruct; | ||
21 | +import java.util.List; | ||
22 | +import java.util.Map; | ||
23 | +import java.util.Optional; | ||
24 | + | ||
25 | +@Component("ZLMediaKitState") | ||
26 | +@TbCoreComponent | ||
27 | +@Slf4j | ||
28 | +public class ZLMediaKitStateRunner { | ||
29 | + private final ZLMediaKitRestFulUtils zlMediaKitRestFulUtils; | ||
30 | + private final TkMediaServerNodeService tkMediaServerNodeService; | ||
31 | + private final TkMediaServerService tkMediaServerService; | ||
32 | + private final MediaConfig mediaConfig; | ||
33 | + private final ZLMediaKitTaskUtils zlMediaKitTaskUtils; | ||
34 | + private final Map<String, Boolean> startGetMedia; | ||
35 | + | ||
36 | + @Value("${media.defaultStateCheckIntervalInSec}") | ||
37 | + @Getter | ||
38 | + private int defaultStateCheckIntervalInSec; | ||
39 | + | ||
40 | + public ZLMediaKitStateRunner( | ||
41 | + ZLMediaKitRestFulUtils zlMediaKitRestFulUtils, | ||
42 | + TkMediaServerNodeService tkMediaServerNodeService, | ||
43 | + TkMediaServerService tkMediaServerService, | ||
44 | + MediaConfig mediaConfig, | ||
45 | + ZLMediaKitTaskUtils zlMediaKitTaskUtils, | ||
46 | + Map<String, Boolean> startGetMedia) { | ||
47 | + this.zlMediaKitRestFulUtils = zlMediaKitRestFulUtils; | ||
48 | + this.tkMediaServerNodeService = tkMediaServerNodeService; | ||
49 | + this.tkMediaServerService = tkMediaServerService; | ||
50 | + this.mediaConfig = mediaConfig; | ||
51 | + this.zlMediaKitTaskUtils = zlMediaKitTaskUtils; | ||
52 | + this.startGetMedia = startGetMedia; | ||
53 | + } | ||
54 | + | ||
55 | + @PostConstruct | ||
56 | + public void init() { | ||
57 | + updateDefaultMediaServer(); | ||
58 | + // 从数据库获取所有的流媒体信息 | ||
59 | + List<MediaServerDTO> dtoList = tkMediaServerService.getAllMediaKit(); | ||
60 | + | ||
61 | + Optional.ofNullable(dtoList).ifPresent( all ->{ | ||
62 | + all.forEach(dto ->{ | ||
63 | + String key = dto.getMediaServerId(); | ||
64 | + startGetMedia.put(key, true); | ||
65 | + zlMediaKitTaskUtils.startCronTask( | ||
66 | + key, | ||
67 | + () -> { | ||
68 | + ZLMediaKitServerConfig zlMediaKitServerConfig = getMediaServerConfig(dto); | ||
69 | + if (null != zlMediaKitServerConfig) { | ||
70 | + startGetMedia.remove(dto.getMediaServerId()); | ||
71 | + zlMediaKitTaskUtils.stop(key); | ||
72 | + zlMediaKitServerConfig.setIp(dto.getIp()); | ||
73 | + zlMediaKitServerConfig.setHttpPort(dto.getHttpPort()); | ||
74 | + zlMediaKitServerConfig.setTenantId(dto.getTenantId()); | ||
75 | + // 进行上线操作 | ||
76 | + tkMediaServerNodeService.zlmServerOnline(zlMediaKitServerConfig); | ||
77 | + } | ||
78 | + }, | ||
79 | + defaultStateCheckIntervalInSec); | ||
80 | + }); | ||
81 | + }); | ||
82 | + } | ||
83 | + | ||
84 | + private void updateDefaultMediaServer() { | ||
85 | + // 默认流媒体服务器 | ||
86 | + MediaServerDTO oldMediaServer = tkMediaServerService.findDefaultMediaServer(); | ||
87 | + MediaServerDTO defaultConfig = mediaConfig.getMediaSerItem(); | ||
88 | + if (oldMediaServer == null) { | ||
89 | + defaultConfig.setTenantId(EntityId.NULL_UUID.toString()); | ||
90 | + }else{ | ||
91 | + defaultConfig.setId(oldMediaServer.getId()); | ||
92 | + defaultConfig.setTenantId(oldMediaServer.getTenantId()); | ||
93 | + } | ||
94 | + tkMediaServerService.saveOrUpdateMediaServer(defaultConfig); | ||
95 | + } | ||
96 | + | ||
97 | + /** | ||
98 | + * 调用流媒体API接口获取流媒体配置 | ||
99 | + * @param mediaServerItem | ||
100 | + * @return | ||
101 | + */ | ||
102 | + private ZLMediaKitServerConfig getMediaServerConfig(MediaServerDTO mediaServerItem) { | ||
103 | + log.error("启动流媒体【ZLMedia】验证,流媒体编号【{}】",mediaServerItem.getMediaServerId()); | ||
104 | + if (startGetMedia == null) { | ||
105 | + return null; | ||
106 | + } | ||
107 | + if (!mediaServerItem.isDefaultServer() | ||
108 | + && tkMediaServerNodeService.getOne(mediaServerItem.getMediaServerId()).isEmpty()) { | ||
109 | + return null; | ||
110 | + } | ||
111 | + if (startGetMedia.get(mediaServerItem.getMediaServerId()) == null | ||
112 | + || !startGetMedia.get(mediaServerItem.getMediaServerId())) { | ||
113 | + return null; | ||
114 | + } | ||
115 | + JsonNode responseJson = zlMediaKitRestFulUtils.getMediaServerConfig(mediaServerItem); | ||
116 | + if(responseJson == null){ | ||
117 | + log.error("流媒体【{}】服务地址【{}:{}】无法访问",mediaServerItem.getMediaServerId(),mediaServerItem.getIp(),mediaServerItem.getHttpPort()); | ||
118 | + return null; | ||
119 | + } | ||
120 | + JsonNode data = responseJson.get(FastIotConstants.ZLMediaBody.DATA); | ||
121 | + if (data != null && !data.isEmpty()) { | ||
122 | + log.error("流媒体【{}:{}】调用成功,响应内容【{}】",mediaServerItem.getIp(),mediaServerItem.getHttpPort(),data); | ||
123 | + return JacksonUtil.convertValue(data.get(0), ZLMediaKitServerConfig.class); | ||
124 | + } | ||
125 | + log.error("流媒体【{}:{}】调用失败,失败原因【{}】",mediaServerItem.getIp(),mediaServerItem.getHttpPort(),responseJson.get(FastIotConstants.ZLMediaBody.MSG)); | ||
126 | + return null; | ||
127 | + } | ||
128 | +} |
@@ -451,6 +451,12 @@ caffeine: | @@ -451,6 +451,12 @@ caffeine: | ||
451 | sceneReact: | 451 | sceneReact: |
452 | timeToLiveInMinutes: "${CACHE_SPECS_SCENE_TTL:1440}" | 452 | timeToLiveInMinutes: "${CACHE_SPECS_SCENE_TTL:1440}" |
453 | maxSize: "${CACHE_SPECS_SCENE_MAX_SIZE:10000}" | 453 | maxSize: "${CACHE_SPECS_SCENE_MAX_SIZE:10000}" |
454 | + tkSipCacheName: | ||
455 | + timeToLiveInMinutes: "${CACHE_SPECS_TK_SIP_TTL:1440}" | ||
456 | + maxSize: "${CACHE_SPECS_TK_SIP_MAX_SIZE:10000}" | ||
457 | + tkMediaServerCacheName: | ||
458 | + timeToLiveInMinutes: "${CACHE_SPECS_TK_MEDIA_SERVER_TTL:1440}" | ||
459 | + maxSize: "${CACHE_SPECS_TK_MEDIA_SERVER_MAX_SIZE:10000}" | ||
454 | redis: | 460 | redis: |
455 | # standalone or cluster | 461 | # standalone or cluster |
456 | connection: | 462 | connection: |
@@ -476,7 +482,7 @@ redis: | @@ -476,7 +482,7 @@ redis: | ||
476 | # db index | 482 | # db index |
477 | db: "${REDIS_DB:0}" | 483 | db: "${REDIS_DB:0}" |
478 | # db password | 484 | # db password |
479 | - password: "${REDIS_PASSWORD:}" | 485 | + password: "${REDIS_PASSWORD:redis@6379}" |
480 | # pool config | 486 | # pool config |
481 | pool_config: | 487 | pool_config: |
482 | maxTotal: "${REDIS_POOL_CONFIG_MAX_TOTAL:128}" | 488 | maxTotal: "${REDIS_POOL_CONFIG_MAX_TOTAL:128}" |
@@ -712,6 +718,9 @@ transport: | @@ -712,6 +718,9 @@ transport: | ||
712 | # Skip certificate validity check for client certificates. | 718 | # Skip certificate validity check for client certificates. |
713 | skip_validity_check_for_client_cert: "${MQTT_SSL_SKIP_VALIDITY_CHECK_FOR_CLIENT_CERT:false}" | 719 | skip_validity_check_for_client_cert: "${MQTT_SSL_SKIP_VALIDITY_CHECK_FOR_CLIENT_CERT:false}" |
714 | # Local CoAP transport parameters | 720 | # Local CoAP transport parameters |
721 | + gbt28181: | ||
722 | + # Enable/disable gbt28181 transport protocol. | ||
723 | + enabled: "${GBT28181_ENABLED:true}" | ||
715 | tcp: | 724 | tcp: |
716 | # Enable/disable tcp transport protocol. | 725 | # Enable/disable tcp transport protocol. |
717 | enabled: "${TCP_ENABLED:true}" | 726 | enabled: "${TCP_ENABLED:true}" |
@@ -1194,10 +1203,10 @@ file: | @@ -1194,10 +1203,10 @@ file: | ||
1194 | type: ${FILE_STORAGE_TYPE:minio} #minio, or other to be implemented | 1203 | type: ${FILE_STORAGE_TYPE:minio} #minio, or other to be implemented |
1195 | randomFileName: ${FILE_STORAGE_FILENAME:true} #是否重命名文件名字,防止冲突 | 1204 | randomFileName: ${FILE_STORAGE_FILENAME:true} #是否重命名文件名字,防止冲突 |
1196 | minio: | 1205 | minio: |
1197 | - minioUrl: ${MINIO_URL:http://127.0.0.1:9000} #minio储存地址 | ||
1198 | - minioName: ${MINIO_NAME:xxxxxx} #minio账户 | ||
1199 | - minioPass: ${MINIO_PWD:xxxxx} #minio访问密码 | ||
1200 | - bucketName: ${MINIO_BUCKET_NAME:yunteng-test} #minio储存桶名称 | 1206 | + minioUrl: ${MINIO_URL:https://demo.thingskit.com:9000} #minio储存地址 |
1207 | + minioName: ${MINIO_NAME:thingskit} #minio账户 | ||
1208 | + minioPass: ${MINIO_PWD:Dzr227+bjsz} #minio访问密码 | ||
1209 | + bucketName: yunteng-test #minio储存桶名称 | ||
1201 | randomFileName: ${file.storage.randomFileName} | 1210 | randomFileName: ${file.storage.randomFileName} |
1202 | account: | 1211 | account: |
1203 | info: | 1212 | info: |
@@ -1218,3 +1227,45 @@ logging: | @@ -1218,3 +1227,45 @@ logging: | ||
1218 | frp: | 1227 | frp: |
1219 | server: | 1228 | server: |
1220 | address: ${FRP_SERVER_ADDRESS:http://127.0.0.1} | 1229 | address: ${FRP_SERVER_ADDRESS:http://127.0.0.1} |
1230 | +sip: | ||
1231 | + # [必须修改] 本机的IP,对应你的网卡,监听什么ip就是使用什么网卡, | ||
1232 | + # 如果要监听多张网卡,可以使用逗号分隔多个IP, 例如: 192.168.1.4,10.0.0.4 | ||
1233 | + # 如果不明白,就使用0.0.0.0,大部分情况都是可以的 | ||
1234 | + # 请不要使用127.0.0.1,任何包括localhost在内的域名都是不可以的。 | ||
1235 | + ip: ${GBT28181_SIP_IP:192.168.1.15} | ||
1236 | + # [可选] 28181服务监听的端口 | ||
1237 | + port: ${GBT28181_SIP_PORT:5060} | ||
1238 | + #[可选] | ||
1239 | + id: ${GBT28181_SIP_ID:44010200492000000001} | ||
1240 | + # 根据国标6.1.2中规定,domain宜采用ID统一编码的前十位编码。国标附录D中定义前8位为中心编码(由省级、市级、区级、基层编号组成,参照GB/T 2260-2007) | ||
1241 | + # 后两位为行业编码,定义参照附录D.3 | ||
1242 | + # 3701020049标识山东济南历下区 信息行业接入 | ||
1243 | + # [可选] | ||
1244 | + domain: ${GBT28181_SIP_DOMAIN:4401020049} | ||
1245 | + #[可选] | ||
1246 | + password: ${GBT28181_SIP_PASSWORD:61332286} | ||
1247 | +#zlm 默认服务器配置 | ||
1248 | +media: | ||
1249 | + id: ${GBT28181_MEDIA_GENERAL_ID:f6GfbO0BGEaROKLP} | ||
1250 | + # [必须修改] zlm服务器的内网IP | ||
1251 | + ip: ${GBT28181_MEDIA_IP:192.168.1.16} | ||
1252 | + # [必须修改] zlm服务器的http.port | ||
1253 | + http-port: ${GBT28181_MEDIA_HTTP_PORT:28080} | ||
1254 | + # [可选] zlm服务器的hook.admin_params=secret | ||
1255 | + secret: ${GBT28181_MEDIA_API_SECRET:5PnbPDCcxQGeK15OowHPPdSgort2Cx9Y} | ||
1256 | + # 启用多端口模式, 多端口模式使用端口区分每路流,兼容性更好。 单端口使用流的ssrc区分, 点播超时建议使用多端口测试 | ||
1257 | + rtp: | ||
1258 | + # [可选] 是否启用多端口模式, 开启后会在portRange范围内选择端口用于媒体流传输 | ||
1259 | + enable: true | ||
1260 | + # [可选] 在此范围内选择端口用于媒体流传输, 必须提前在zlm上配置该属性,不然自动配置此属性可能不成功 | ||
1261 | + port-range: ${GBT28181_MEDIA_RTP_PORT_RANGE:30000,30500} # 端口范围 | ||
1262 | + # [可选] 国标级联在此范围内选择端口发送媒体流, | ||
1263 | + send-port-range: ${GBT28181_MEDIA_RTP_PORT_RANGE:30000,30500} # 端口范围 | ||
1264 | + # 录像辅助服务, 部署此服务可以实现zlm录像的管理与下载, 0 表示不使用 | ||
1265 | + record-assist-port: 0 | ||
1266 | + defaultStateCheckIntervalInSec: "${DEFAULT_STATE_CHECK_INTERVAL:10}" | ||
1267 | + | ||
1268 | +thingskit: | ||
1269 | + release: | ||
1270 | + version: v1.1.1 Release | ||
1271 | + date: 20230630 |
@@ -8,7 +8,7 @@ | @@ -8,7 +8,7 @@ | ||
8 | * http://www.apache.org/licenses/LICENSE-2.0 | 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
9 | * | 9 | * |
10 | * Unless required by applicable law or agreed to in writing, software | 10 | * Unless required by applicable law or agreed to in writing, software |
11 | - * distributed under the License is distributed on an "AS IS" BASIS, | 11 | + * distributed under the License is distributed on an "AS IS" BASIS,in |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
13 | * See the License for the specific language governing permissions and | 13 | * See the License for the specific language governing permissions and |
14 | * limitations under the License. | 14 | * limitations under the License. |
@@ -130,10 +130,7 @@ message SessionEventMsg { | @@ -130,10 +130,7 @@ message SessionEventMsg { | ||
130 | SessionEvent event = 2; | 130 | SessionEvent event = 2; |
131 | } | 131 | } |
132 | 132 | ||
133 | -//thingskit | ||
134 | -message PostEventMsg { | ||
135 | - repeated KeyValueProto kv = 1; | ||
136 | -} | 133 | + |
137 | message PostTelemetryMsg { | 134 | message PostTelemetryMsg { |
138 | repeated TsKvListProto tsKvList = 1; | 135 | repeated TsKvListProto tsKvList = 1; |
139 | } | 136 | } |
@@ -682,6 +679,7 @@ message TransportApiRequestMsg { | @@ -682,6 +679,7 @@ message TransportApiRequestMsg { | ||
682 | 679 | ||
683 | //Thingskit function | 680 | //Thingskit function |
684 | ScriptProto script = 14; | 681 | ScriptProto script = 14; |
682 | + Gbt28181RequestMsg gbt28181RequestMsg = 15; | ||
685 | } | 683 | } |
686 | 684 | ||
687 | /* Response from ThingsBoard Core Service to Transport Service */ | 685 | /* Response from ThingsBoard Core Service to Transport Service */ |
@@ -699,6 +697,7 @@ message TransportApiResponseMsg { | @@ -699,6 +697,7 @@ message TransportApiResponseMsg { | ||
699 | 697 | ||
700 | //Thingskit function | 698 | //Thingskit function |
701 | repeated ScriptProto scriptsResponseMsg = 11; | 699 | repeated ScriptProto scriptsResponseMsg = 11; |
700 | + Gbt28181ResponseMsg gbt28181ResponseMsg = 12; | ||
702 | } | 701 | } |
703 | 702 | ||
704 | /* Messages that are handled by ThingsBoard Core Service */ | 703 | /* Messages that are handled by ThingsBoard Core Service */ |
@@ -795,3 +794,37 @@ message ScriptProto{ | @@ -795,3 +794,37 @@ message ScriptProto{ | ||
795 | string convertJs = 6; | 794 | string convertJs = 6; |
796 | int32 status = 7; | 795 | int32 status = 7; |
797 | } | 796 | } |
797 | +//thingskit function 设备上报的物模型事件 | ||
798 | +message PostEventMsg { | ||
799 | + repeated KeyValueProto kv = 1; | ||
800 | +} | ||
801 | +message Gbt28181RequestMsg{ | ||
802 | + bytes context = 1; | ||
803 | + string contextType = 2; | ||
804 | + string callId = 3; | ||
805 | + string fromTag = 4; | ||
806 | + string toTag = 5; | ||
807 | + string viaTag = 6; | ||
808 | + string viaBranch = 7; | ||
809 | + int32 sn = 8; | ||
810 | + int64 tenantIdMSB = 9; | ||
811 | + int64 tenantIdLSB = 10; | ||
812 | + int64 entityIdMSB = 11; | ||
813 | + int64 entityIdLSB = 12; | ||
814 | +} | ||
815 | +message Gbt28181ResponseMsg{ | ||
816 | + bytes context = 1; | ||
817 | + string contextType = 2; | ||
818 | + string callId = 3; | ||
819 | + string fromTag = 4; | ||
820 | + string toTag = 5; | ||
821 | + string viaTag = 6; | ||
822 | + string viaBranch = 7; | ||
823 | + int32 sn = 8; | ||
824 | + int64 tenantIdMSB = 9; | ||
825 | + int64 tenantIdLSB = 10; | ||
826 | + int64 entityIdMSB = 11; | ||
827 | + int64 entityIdLSB = 12; | ||
828 | +} | ||
829 | + | ||
830 | + |
@@ -48,6 +48,8 @@ public class DataConstants { | @@ -48,6 +48,8 @@ public class DataConstants { | ||
48 | public static final String HTTP_TRANSPORT_NAME = "HTTP"; | 48 | public static final String HTTP_TRANSPORT_NAME = "HTTP"; |
49 | public static final String SNMP_TRANSPORT_NAME = "SNMP"; | 49 | public static final String SNMP_TRANSPORT_NAME = "SNMP"; |
50 | 50 | ||
51 | + public static final String GBT_28181 = "GBT28181"; | ||
52 | + | ||
51 | 53 | ||
52 | public static final String[] allScopes() { | 54 | public static final String[] allScopes() { |
53 | return new String[]{CLIENT_SCOPE, SHARED_SCOPE, SERVER_SCOPE}; | 55 | return new String[]{CLIENT_SCOPE, SHARED_SCOPE, SERVER_SCOPE}; |
@@ -34,6 +34,7 @@ import java.io.Serializable; | @@ -34,6 +34,7 @@ import java.io.Serializable; | ||
34 | @JsonSubTypes.Type(value = DefaultDeviceTransportConfiguration.class, name = "DEFAULT"), | 34 | @JsonSubTypes.Type(value = DefaultDeviceTransportConfiguration.class, name = "DEFAULT"), |
35 | @JsonSubTypes.Type(value = MqttDeviceTransportConfiguration.class, name = "MQTT"), | 35 | @JsonSubTypes.Type(value = MqttDeviceTransportConfiguration.class, name = "MQTT"), |
36 | @JsonSubTypes.Type(value = TkTcpDeviceTransportConfiguration.class, name = "TCP"), | 36 | @JsonSubTypes.Type(value = TkTcpDeviceTransportConfiguration.class, name = "TCP"), |
37 | + @JsonSubTypes.Type(value = TkGBTDeviceTransportConfiguration.class, name = "GBT28181"), | ||
37 | @JsonSubTypes.Type(value = CoapDeviceTransportConfiguration.class, name = "COAP"), | 38 | @JsonSubTypes.Type(value = CoapDeviceTransportConfiguration.class, name = "COAP"), |
38 | @JsonSubTypes.Type(value = Lwm2mDeviceTransportConfiguration.class, name = "LWM2M"), | 39 | @JsonSubTypes.Type(value = Lwm2mDeviceTransportConfiguration.class, name = "LWM2M"), |
39 | @JsonSubTypes.Type(value = SnmpDeviceTransportConfiguration.class, name = "SNMP")}) | 40 | @JsonSubTypes.Type(value = SnmpDeviceTransportConfiguration.class, name = "SNMP")}) |
1 | +/** | ||
2 | + * Copyright © 2016-2022 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.common.data.device.data; | ||
17 | +import lombok.Data; | ||
18 | +import org.thingsboard.server.common.data.DeviceTransportType; | ||
19 | + | ||
20 | +@Data | ||
21 | +public class TkGBTDeviceTransportConfiguration implements DeviceTransportConfiguration { | ||
22 | + @Override | ||
23 | + public DeviceTransportType getType() { | ||
24 | + return DeviceTransportType.GBT28181; | ||
25 | + } | ||
26 | + | ||
27 | +} |
@@ -32,6 +32,7 @@ import java.io.Serializable; | @@ -32,6 +32,7 @@ import java.io.Serializable; | ||
32 | @JsonSubTypes.Type(value = DefaultDeviceProfileTransportConfiguration.class, name = "DEFAULT"), | 32 | @JsonSubTypes.Type(value = DefaultDeviceProfileTransportConfiguration.class, name = "DEFAULT"), |
33 | @JsonSubTypes.Type(value = MqttDeviceProfileTransportConfiguration.class, name = "MQTT"), | 33 | @JsonSubTypes.Type(value = MqttDeviceProfileTransportConfiguration.class, name = "MQTT"), |
34 | @JsonSubTypes.Type(value = TkTcpDeviceProfileTransportConfiguration.class, name = "TCP"), | 34 | @JsonSubTypes.Type(value = TkTcpDeviceProfileTransportConfiguration.class, name = "TCP"), |
35 | + @JsonSubTypes.Type(value = TkGBT28181DeviceProfileTransportConfiguration.class, name = "GBT28181"), | ||
35 | @JsonSubTypes.Type(value = Lwm2mDeviceProfileTransportConfiguration.class, name = "LWM2M"), | 36 | @JsonSubTypes.Type(value = Lwm2mDeviceProfileTransportConfiguration.class, name = "LWM2M"), |
36 | @JsonSubTypes.Type(value = CoapDeviceProfileTransportConfiguration.class, name = "COAP"), | 37 | @JsonSubTypes.Type(value = CoapDeviceProfileTransportConfiguration.class, name = "COAP"), |
37 | @JsonSubTypes.Type(value = SnmpDeviceProfileTransportConfiguration.class, name = "SNMP") | 38 | @JsonSubTypes.Type(value = SnmpDeviceProfileTransportConfiguration.class, name = "SNMP") |
1 | +package org.thingsboard.server.common.data.device.profile; | ||
2 | + | ||
3 | +import lombok.Data; | ||
4 | +import org.thingsboard.server.common.data.DeviceTransportType; | ||
5 | + | ||
6 | +@Data | ||
7 | +public class TkGBT28181DeviceProfileTransportConfiguration implements DeviceProfileTransportConfiguration { | ||
8 | + @Override | ||
9 | + public DeviceTransportType getType() { | ||
10 | + return DeviceTransportType.GBT28181; | ||
11 | + } | ||
12 | + | ||
13 | + | ||
14 | +} |
1 | package org.thingsboard.server.common.data.yunteng.common; | 1 | package org.thingsboard.server.common.data.yunteng.common; |
2 | 2 | ||
3 | import org.thingsboard.server.common.data.yunteng.dto.SysDictItemDTO; | 3 | import org.thingsboard.server.common.data.yunteng.dto.SysDictItemDTO; |
4 | +import org.thingsboard.server.common.data.yunteng.dto.sip.MediaServerDTO; | ||
4 | 5 | ||
5 | public interface TkCommonService { | 6 | public interface TkCommonService { |
6 | /** | 7 | /** |
@@ -11,6 +12,4 @@ public interface TkCommonService { | @@ -11,6 +12,4 @@ public interface TkCommonService { | ||
11 | * @return 返回字典Item表 | 12 | * @return 返回字典Item表 |
12 | */ | 13 | */ |
13 | SysDictItemDTO getDictValueByCodeAndText(String dictCode, String codeValue); | 14 | SysDictItemDTO getDictValueByCodeAndText(String dictCode, String codeValue); |
14 | - | ||
15 | - | ||
16 | } | 15 | } |
1 | +package org.thingsboard.server.common.data.yunteng.common.media; | ||
2 | + | ||
3 | +import java.util.*; | ||
4 | +import lombok.extern.slf4j.Slf4j; | ||
5 | +import org.springframework.beans.factory.annotation.Autowired; | ||
6 | +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; | ||
7 | +import org.springframework.stereotype.Component; | ||
8 | +import org.thingsboard.server.common.data.yunteng.config.media.UserSetting; | ||
9 | +import org.thingsboard.server.common.data.yunteng.constant.FastIotConstants; | ||
10 | +import org.thingsboard.server.common.data.yunteng.core.cache.CacheUtils; | ||
11 | +import org.thingsboard.server.common.data.yunteng.dto.sip.SipMessageHeaderDTO; | ||
12 | +import org.thingsboard.server.common.data.yunteng.dto.sip.SsrcTransactionDTO; | ||
13 | +import org.thingsboard.server.common.data.yunteng.enums.SessionTypeEnum; | ||
14 | + | ||
15 | +/** 视频流session管理器,管理视频预览、预览回放的通信句柄 */ | ||
16 | +@Component | ||
17 | +@Slf4j | ||
18 | +@ConditionalOnExpression( | ||
19 | + "'${service.type:null}'=='tb-transport' || ('${service.type:null}'=='monolith' && '${transport.api_enabled:true}'=='true' && '${transport.gbt28181.enabled}'=='true')") | ||
20 | +public class VideoStreamSessionManager { | ||
21 | + | ||
22 | + @Autowired private UserSetting userSetting; | ||
23 | + @Autowired private CacheUtils cacheUtils; | ||
24 | + private final String cacheName = FastIotConstants.MediaServerKey.MEDIA_SERVER_CACHE_NAME; | ||
25 | + | ||
26 | + /** | ||
27 | + * 添加一个点播/回放的事务信息 后续可以通过流Id/callID | ||
28 | + * | ||
29 | + * @param cameraCode 设备ID | ||
30 | + * @param channelId 通道ID | ||
31 | + * @param callId 一次请求的CallID | ||
32 | + * @param stream 流名称 | ||
33 | + * @param mediaServerId 所使用的流媒体ID | ||
34 | + */ | ||
35 | + public void put( | ||
36 | + String cameraCode, | ||
37 | + String channelId, | ||
38 | + String callId, | ||
39 | + String stream, | ||
40 | + String ssrc, | ||
41 | + SipMessageHeaderDTO sipMessage, | ||
42 | + String mediaServerId, | ||
43 | + SessionTypeEnum type) { | ||
44 | + | ||
45 | + SsrcTransactionDTO ssrcTransaction = new SsrcTransactionDTO(); | ||
46 | + ssrcTransaction.setCameraCode(cameraCode); | ||
47 | + ssrcTransaction.setChannelId(channelId); | ||
48 | + ssrcTransaction.setStream(stream); | ||
49 | + ssrcTransaction.setSipTransactionInfo(sipMessage); | ||
50 | + ssrcTransaction.setCallId(callId); | ||
51 | + ssrcTransaction.setSsrc(ssrc); | ||
52 | + ssrcTransaction.setMediaServerId(mediaServerId); | ||
53 | + ssrcTransaction.setType(type); | ||
54 | + String fullKey = buildTransactionFullKey(cameraCode, channelId, callId, stream); | ||
55 | + String channelKey = buildTransactionChannelKey(cameraCode, channelId, stream); | ||
56 | + String streamKey = buildTransactionStreamKey(stream); | ||
57 | + String cameraKey = buildTransactionCameraKey(cameraCode); | ||
58 | + | ||
59 | + freshTransactionKey(cameraKey, fullKey, false); | ||
60 | + freshTransactionKey(streamKey, fullKey, false); | ||
61 | + freshTransactionKey(channelKey, fullKey, false); | ||
62 | + cacheUtils.put(cacheName, fullKey, ssrcTransaction); | ||
63 | + } | ||
64 | + | ||
65 | + private void freshTransactionKey(String key, String val, boolean remove) { | ||
66 | + Optional<Set<String>> cameraDatas = cacheUtils.get(cacheName, key); | ||
67 | + Set<String> values = new HashSet<>(); | ||
68 | + cameraDatas.ifPresent(values::addAll); | ||
69 | + if (remove) { | ||
70 | + values.remove(val); | ||
71 | + } else { | ||
72 | + values.add(val); | ||
73 | + } | ||
74 | + cacheUtils.put(cacheName, key, values); | ||
75 | + } | ||
76 | + | ||
77 | + public Optional<Set<String>> getSsrcTransactionCamaraKey(String cameraCode) { | ||
78 | + return cacheUtils.get(cacheName, buildTransactionCameraKey(cameraCode)); | ||
79 | + } | ||
80 | + | ||
81 | + public Optional<Set<String>> getSsrcTransactionStreamKey(String stream) { | ||
82 | + return cacheUtils.get(cacheName, buildTransactionStreamKey(stream)); | ||
83 | + } | ||
84 | + | ||
85 | + public Optional<Set<String>> getSsrcTransactionChannelKey( | ||
86 | + String cameraCode, String channelId, String stream) { | ||
87 | + return cacheUtils.get(cacheName, buildTransactionChannelKey(cameraCode, channelId, stream)); | ||
88 | + } | ||
89 | + | ||
90 | + public Optional<SsrcTransactionDTO> getSsrcTransaction(String fullKey) { | ||
91 | + return cacheUtils.get(cacheName, fullKey); | ||
92 | + } | ||
93 | + public Optional<SsrcTransactionDTO> getSsrcTransaction(String cameraCode, String channelId, String stream) { | ||
94 | + Optional<Set<String>> keys =getSsrcTransactionChannelKey(cameraCode, channelId,stream); | ||
95 | + | ||
96 | + if(keys.isPresent()){ | ||
97 | + Optional<String> fullKey =keys.get().stream().findFirst(); | ||
98 | + if(fullKey.isPresent()){ | ||
99 | + return getSsrcTransaction(fullKey.get()); | ||
100 | + } | ||
101 | + } | ||
102 | + return Optional.empty(); | ||
103 | + } | ||
104 | + | ||
105 | + public String getMediaServerId(String deviceId, String channelId, String stream) { | ||
106 | + Optional<Set<String>> channels = getSsrcTransactionChannelKey(deviceId, channelId, stream); | ||
107 | + if (channels.isPresent()) { | ||
108 | + for (String fullKey : channels.get()) { | ||
109 | + Optional<SsrcTransactionDTO> result = getSsrcTransaction(fullKey); | ||
110 | + if (result.isPresent()) { | ||
111 | + return result.get().getMediaServerId(); | ||
112 | + } | ||
113 | + } | ||
114 | + } | ||
115 | + return null; | ||
116 | + } | ||
117 | + | ||
118 | + public String getSSRC(String deviceId, String channelId, String stream) { | ||
119 | + Optional<Set<String>> channels = getSsrcTransactionChannelKey(deviceId, channelId, stream); | ||
120 | + | ||
121 | + if (channels.isPresent()) { | ||
122 | + for (String fullKey : channels.get()) { | ||
123 | + Optional<SsrcTransactionDTO> result = getSsrcTransaction(fullKey); | ||
124 | + if (result.isPresent()) { | ||
125 | + return result.get().getSsrc(); | ||
126 | + } | ||
127 | + } | ||
128 | + } | ||
129 | + return null; | ||
130 | + } | ||
131 | + | ||
132 | + public void remove(String cameraCode, String channelId, String stream) { | ||
133 | + Optional<Set<String>> channelKeys = getSsrcTransactionChannelKey(cameraCode, channelId, stream); | ||
134 | + channelKeys.ifPresent( | ||
135 | + keys -> { | ||
136 | + keys.forEach( | ||
137 | + fullKey -> { | ||
138 | + String streamKey = buildTransactionStreamKey(stream); | ||
139 | + String cameraKey = buildTransactionCameraKey(cameraCode); | ||
140 | + String channelKey = buildTransactionChannelKey(cameraCode, channelId, stream); | ||
141 | + freshTransactionKey(cameraKey, fullKey, true); | ||
142 | + freshTransactionKey(streamKey, fullKey, true); | ||
143 | + freshTransactionKey(channelKey, fullKey, true); | ||
144 | + cacheUtils.invalidate(cacheName, fullKey); | ||
145 | + }); | ||
146 | + }); | ||
147 | + } | ||
148 | + | ||
149 | + public String buildTransactionFullKey( | ||
150 | + String cameraCode, String channelId, String callId, String stream) { | ||
151 | + return FastIotConstants.MediaServerKey.MEDIA_TRANSACTION_USED_PREFIX | ||
152 | + + userSetting.getServerId() | ||
153 | + + "_" | ||
154 | + + cameraCode | ||
155 | + + "_" | ||
156 | + + channelId | ||
157 | + + "_" | ||
158 | + + callId | ||
159 | + + "_" | ||
160 | + + stream; | ||
161 | + } | ||
162 | + | ||
163 | + public String buildTransactionChannelKey(String cameraCode, String channelId, String stream) { | ||
164 | + return FastIotConstants.MediaServerKey.MEDIA_TRANSACTION_USED_PREFIX | ||
165 | + + userSetting.getServerId() | ||
166 | + + "_" | ||
167 | + + cameraCode | ||
168 | + + "_" | ||
169 | + + channelId | ||
170 | + + "_" | ||
171 | + + stream; | ||
172 | + } | ||
173 | + | ||
174 | + public String buildTransactionStreamKey(String stream) { | ||
175 | + return FastIotConstants.MediaServerKey.MEDIA_TRANSACTION_USED_PREFIX | ||
176 | + + userSetting.getServerId() | ||
177 | + + "_" | ||
178 | + + stream; | ||
179 | + } | ||
180 | + | ||
181 | + public String buildTransactionCameraKey(String cameraCode) { | ||
182 | + return FastIotConstants.MediaServerKey.MEDIA_TRANSACTION_USED_PREFIX | ||
183 | + + userSetting.getServerId() | ||
184 | + + "_" | ||
185 | + + cameraCode; | ||
186 | + } | ||
187 | + | ||
188 | + public String getReceiveRtpKey(String ssrcStream) { | ||
189 | + return FastIotConstants.CacheSipKey.TK_OTHER_RECEIVE_RTP_INFO | ||
190 | + + userSetting.getServerId() | ||
191 | + + "_" | ||
192 | + + ssrcStream; | ||
193 | + } | ||
194 | + | ||
195 | + public String getReceivePsKey(String ssrcStream) { | ||
196 | + return FastIotConstants.CacheSipKey.TK_OTHER_RECEIVE_PS_INFO | ||
197 | + + userSetting.getServerId() | ||
198 | + + "_" | ||
199 | + + ssrcStream; | ||
200 | + } | ||
201 | +} |
1 | +package org.thingsboard.server.common.data.yunteng.common.media; | ||
2 | + | ||
3 | +import java.time.Instant; | ||
4 | +import java.time.LocalDateTime; | ||
5 | +import java.time.ZoneId; | ||
6 | +import java.util.*; | ||
7 | +import java.util.concurrent.ConcurrentHashMap; | ||
8 | +import java.util.concurrent.TimeUnit; | ||
9 | + | ||
10 | +import com.fasterxml.jackson.databind.JsonNode; | ||
11 | +import com.fasterxml.jackson.databind.node.ObjectNode; | ||
12 | +import org.springframework.scheduling.annotation.Scheduled; | ||
13 | +import org.springframework.stereotype.Component; | ||
14 | +import org.springframework.util.CollectionUtils; | ||
15 | +import org.thingsboard.server.common.data.yunteng.dto.sip.MediaServerDTO; | ||
16 | +import org.thingsboard.server.common.data.yunteng.dto.sip.hook.IHookSubscribe; | ||
17 | +import org.thingsboard.server.common.data.yunteng.enums.HookTypeEnum; | ||
18 | + | ||
19 | +/** | ||
20 | + * ZLMediaServer的hook事件订阅 | ||
21 | + * | ||
22 | + * @author lin | ||
23 | + */ | ||
24 | +@Component | ||
25 | +public class ZlmHttpHookSubscribe { | ||
26 | + | ||
27 | + @FunctionalInterface | ||
28 | + public interface Event { | ||
29 | + void response(MediaServerDTO mediaServerInfo, JsonNode response); | ||
30 | + } | ||
31 | + | ||
32 | + private Map<HookTypeEnum, Map<IHookSubscribe, Event>> allSubscribes = new ConcurrentHashMap<>(); | ||
33 | + | ||
34 | + public void addSubscribe(IHookSubscribe hookSubscribe, Event event) { | ||
35 | + if (hookSubscribe.getExpires() == null) { | ||
36 | + // 默认5分钟过期 | ||
37 | + Instant expiresInstant = Instant.now().plusSeconds(TimeUnit.MINUTES.toSeconds(5)); | ||
38 | + hookSubscribe.setExpires(LocalDateTime.ofInstant(expiresInstant, ZoneId.systemDefault())); | ||
39 | + } | ||
40 | + allSubscribes | ||
41 | + .computeIfAbsent(hookSubscribe.getHookType(), k -> new ConcurrentHashMap<>()) | ||
42 | + .put(hookSubscribe, event); | ||
43 | + } | ||
44 | + | ||
45 | + public Event sendNotify(HookTypeEnum type, JsonNode hookResponse) { | ||
46 | + Event event = null; | ||
47 | + Map<IHookSubscribe, Event> eventMap = allSubscribes.get(type); | ||
48 | + if (eventMap == null) { | ||
49 | + return null; | ||
50 | + } | ||
51 | + for (IHookSubscribe key : eventMap.keySet()) { | ||
52 | + Boolean result = null; | ||
53 | + // 使用 fields() 方法获取键值对迭代器 | ||
54 | + Iterator<Map.Entry<String, JsonNode>> fieldsIterator = key.getContent().fields(); | ||
55 | + while (fieldsIterator.hasNext()) { | ||
56 | + Map.Entry<String, JsonNode> field = fieldsIterator.next(); | ||
57 | + String s = field.getKey(); | ||
58 | + JsonNode value = field.getValue(); | ||
59 | + if (result == null) { | ||
60 | + result = value.equals(hookResponse.get(s)); | ||
61 | + } else { | ||
62 | + if (value == null) { | ||
63 | + continue; | ||
64 | + } | ||
65 | + result = result && value.equals(hookResponse.get(s)); | ||
66 | + } | ||
67 | + } | ||
68 | + if (null != result && result) { | ||
69 | + event = eventMap.get(key); | ||
70 | + } | ||
71 | + } | ||
72 | + return event; | ||
73 | + } | ||
74 | + | ||
75 | + public void removeSubscribe(IHookSubscribe hookSubscribe) { | ||
76 | + Map<IHookSubscribe, Event> eventMap = allSubscribes.get(hookSubscribe.getHookType()); | ||
77 | + if (eventMap == null) { | ||
78 | + return; | ||
79 | + } | ||
80 | + | ||
81 | + Set<Map.Entry<IHookSubscribe, Event>> entries = eventMap.entrySet(); | ||
82 | + if (entries.size() > 0) { | ||
83 | + List<Map.Entry<IHookSubscribe, Event>> entriesToRemove = new ArrayList<>(); | ||
84 | + for (Map.Entry<IHookSubscribe, Event> entry : entries) { | ||
85 | + ObjectNode content = entry.getKey().getContent(); | ||
86 | + if (content == null || content.size() == 0) { | ||
87 | + entriesToRemove.add(entry); | ||
88 | + continue; | ||
89 | + } | ||
90 | + Boolean result = null; | ||
91 | + Iterator<Map.Entry<String, JsonNode>> fieldsIterator = content.fields(); | ||
92 | + while (fieldsIterator.hasNext()) { | ||
93 | + Map.Entry<String, JsonNode> field = fieldsIterator.next(); | ||
94 | + String s = field.getKey(); | ||
95 | + JsonNode value = field.getValue(); | ||
96 | + if (result == null) { | ||
97 | + result = value.equals(hookSubscribe.getContent().get(s)); | ||
98 | + } else { | ||
99 | + if (value == null) { | ||
100 | + continue; | ||
101 | + } | ||
102 | + result = result && value.equals(hookSubscribe.getContent().get(s)); | ||
103 | + } | ||
104 | + } | ||
105 | + if (result) { | ||
106 | + entriesToRemove.add(entry); | ||
107 | + } | ||
108 | + } | ||
109 | + | ||
110 | + if (!CollectionUtils.isEmpty(entriesToRemove)) { | ||
111 | + for (Map.Entry<IHookSubscribe, Event> entry : entriesToRemove) { | ||
112 | + entries.remove(entry); | ||
113 | + } | ||
114 | + } | ||
115 | + } | ||
116 | + } | ||
117 | + | ||
118 | + /** | ||
119 | + * 获取某个类型的所有的订阅 | ||
120 | + * | ||
121 | + * @param type | ||
122 | + * @return | ||
123 | + */ | ||
124 | + public List<Event> getSubscribes(HookTypeEnum type) { | ||
125 | + Map<IHookSubscribe, Event> eventMap = allSubscribes.get(type); | ||
126 | + if (eventMap == null) { | ||
127 | + return null; | ||
128 | + } | ||
129 | + List<Event> result = new ArrayList<>(); | ||
130 | + for (IHookSubscribe key : eventMap.keySet()) { | ||
131 | + result.add(eventMap.get(key)); | ||
132 | + } | ||
133 | + return result; | ||
134 | + } | ||
135 | + | ||
136 | + public List<IHookSubscribe> getAll() { | ||
137 | + ArrayList<IHookSubscribe> result = new ArrayList<>(); | ||
138 | + Collection<Map<IHookSubscribe, Event>> values = allSubscribes.values(); | ||
139 | + for (Map<IHookSubscribe, Event> value : values) { | ||
140 | + result.addAll(value.keySet()); | ||
141 | + } | ||
142 | + return result; | ||
143 | + } | ||
144 | + | ||
145 | + /** 对订阅数据进行过期清理 */ | ||
146 | + @Scheduled(cron = "0 0/5 * * * ?") // 每5分钟执行一次 | ||
147 | + public void execute() { | ||
148 | + | ||
149 | + Instant instant = Instant.now().minusMillis(TimeUnit.MINUTES.toMillis(5)); | ||
150 | + LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, ZoneId.systemDefault()); | ||
151 | + int total = 0; | ||
152 | + for (HookTypeEnum hookType : allSubscribes.keySet()) { | ||
153 | + Map<IHookSubscribe, Event> hookSubscribeEventMap = allSubscribes.get(hookType); | ||
154 | + if (hookSubscribeEventMap.size() > 0) { | ||
155 | + for (IHookSubscribe hookSubscribe : hookSubscribeEventMap.keySet()) { | ||
156 | + if (hookSubscribe.getExpires().isBefore(localDateTime)) { | ||
157 | + // 过期的 | ||
158 | + hookSubscribeEventMap.remove(hookSubscribe); | ||
159 | + total++; | ||
160 | + } | ||
161 | + } | ||
162 | + } | ||
163 | + } | ||
164 | + } | ||
165 | +} |
common/data/src/main/java/org/thingsboard/server/common/data/yunteng/config/media/MediaConfig.java
0 → 100644
1 | +package org.thingsboard.server.common.data.yunteng.config.media; | ||
2 | + | ||
3 | +import lombok.Data; | ||
4 | +import lombok.extern.slf4j.Slf4j; | ||
5 | +import org.springframework.beans.factory.annotation.Value; | ||
6 | +import org.springframework.boot.context.properties.ConfigurationProperties; | ||
7 | +import org.springframework.stereotype.Component; | ||
8 | +import org.springframework.util.ObjectUtils; | ||
9 | +import org.thingsboard.server.common.data.yunteng.dto.sip.MediaServerDTO; | ||
10 | + | ||
11 | +import java.net.InetAddress; | ||
12 | +import java.net.UnknownHostException; | ||
13 | +import java.util.Objects; | ||
14 | +import java.util.regex.Pattern; | ||
15 | + | ||
16 | +/** | ||
17 | + * 配置文件里面的流媒体配置信息 | ||
18 | + */ | ||
19 | +@ConfigurationProperties(prefix = "media") | ||
20 | +@Component | ||
21 | +@Data | ||
22 | +@Slf4j | ||
23 | +public class MediaConfig { | ||
24 | + @Value("${media.id}") | ||
25 | + private String mediaServerId; | ||
26 | + | ||
27 | + @Value("${media.ip}") | ||
28 | + private String ip; | ||
29 | + | ||
30 | + @Value("${media.hook-ip:}") | ||
31 | + private String hookIp; | ||
32 | + | ||
33 | + @Value("${sip.ip}") | ||
34 | + private String sipIp; | ||
35 | + | ||
36 | + @Value("${sip.domain}") | ||
37 | + private String sipDomain; | ||
38 | + | ||
39 | + @Value("${media.sdp-ip:${media.ip}}") | ||
40 | + private String sdpIp; | ||
41 | + | ||
42 | + @Value("${media.stream-ip:${media.ip}}") | ||
43 | + private String streamIp; | ||
44 | + | ||
45 | + @Value("${media.http-port}") | ||
46 | + private Integer httpPort; | ||
47 | + | ||
48 | + @Value("${media.http-ssl-port:0}") | ||
49 | + private Integer httpSslPort = 0; | ||
50 | + | ||
51 | + @Value("${media.rtmp-port:0}") | ||
52 | + private Integer rtmpPort = 0; | ||
53 | + | ||
54 | + @Value("${media.rtmp-ssl-port:0}") | ||
55 | + private Integer rtmpSslPort = 0; | ||
56 | + | ||
57 | + @Value("${media.rtp-proxy-port:0}") | ||
58 | + private Integer rtpProxyPort = 0; | ||
59 | + | ||
60 | + @Value("${media.rtsp-port:0}") | ||
61 | + private Integer rtspPort = 0; | ||
62 | + | ||
63 | + @Value("${media.rtsp-ssl-port:0}") | ||
64 | + private Integer rtspSslPort = 0; | ||
65 | + | ||
66 | + @Value("${media.auto-config:true}") | ||
67 | + private boolean autoConfig = true; | ||
68 | + | ||
69 | + @Value("${media.secret}") | ||
70 | + private String secret; | ||
71 | + | ||
72 | + @Value("${media.rtp.enable}") | ||
73 | + private boolean rtpEnable; | ||
74 | + | ||
75 | + @Value("${media.rtp.port-range}") | ||
76 | + private String rtpPortRange; | ||
77 | + | ||
78 | + @Value("${media.rtp.send-port-range}") | ||
79 | + private String rtpSendPortRange; | ||
80 | + | ||
81 | + @Value("${media.record-assist-port:0}") | ||
82 | + private Integer recordAssistPort = 0; | ||
83 | + private boolean status; | ||
84 | + | ||
85 | + public MediaServerDTO getMediaSerItem() { | ||
86 | + MediaServerDTO mediaServerItem = new MediaServerDTO(); | ||
87 | + mediaServerItem.setMediaServerId(mediaServerId); | ||
88 | + mediaServerItem.setIp(ip); | ||
89 | + mediaServerItem.setDefaultServer(true); | ||
90 | + mediaServerItem.setHookIp(getHookIp()); | ||
91 | + mediaServerItem.setSdpIp(getSdpIp()); | ||
92 | + mediaServerItem.setStreamIp(getStreamIp()); | ||
93 | + mediaServerItem.setHttpPort(httpPort); | ||
94 | + mediaServerItem.setHttpSslPort(httpSslPort); | ||
95 | + mediaServerItem.setRtmpPort(rtmpPort); | ||
96 | + mediaServerItem.setRtmpSslPort(rtmpSslPort); | ||
97 | + mediaServerItem.setRtpProxyPort(getRtpProxyPort()); | ||
98 | + mediaServerItem.setRtspPort(rtspPort); | ||
99 | + mediaServerItem.setRtspSslPort(rtspSslPort); | ||
100 | + mediaServerItem.setAutoConfig(autoConfig); | ||
101 | + mediaServerItem.setSecret(secret); | ||
102 | + mediaServerItem.setRtpEnable(rtpEnable); | ||
103 | + mediaServerItem.setRtpPortRange(rtpPortRange); | ||
104 | + mediaServerItem.setSendRtpPortRange(rtpSendPortRange); | ||
105 | + mediaServerItem.setRecordAssistPort(recordAssistPort); | ||
106 | + mediaServerItem.setHookAliveInterval(30.00f); | ||
107 | + mediaServerItem.setStatus(status); | ||
108 | + return mediaServerItem; | ||
109 | + } | ||
110 | + | ||
111 | + public String getHookIp() { | ||
112 | + if (ObjectUtils.isEmpty(hookIp)) { | ||
113 | + return sipIp.split(",")[0]; | ||
114 | + } else { | ||
115 | + return hookIp; | ||
116 | + } | ||
117 | + } | ||
118 | + | ||
119 | + public String getSipIp() { | ||
120 | + if (sipIp == null) { | ||
121 | + return this.ip; | ||
122 | + } else { | ||
123 | + return sipIp; | ||
124 | + } | ||
125 | + } | ||
126 | + | ||
127 | + public int getRtpProxyPort() { | ||
128 | + return Objects.requireNonNullElse(rtpProxyPort, 0); | ||
129 | + } | ||
130 | + | ||
131 | + public String getSdpIp() { | ||
132 | + if (ObjectUtils.isEmpty(sdpIp)) { | ||
133 | + return ip; | ||
134 | + } else { | ||
135 | + if (isValidIPAddress(sdpIp)) { | ||
136 | + return sdpIp; | ||
137 | + } else { | ||
138 | + // 按照域名解析 | ||
139 | + String hostAddress = null; | ||
140 | + try { | ||
141 | + hostAddress = InetAddress.getByName(sdpIp).getHostAddress(); | ||
142 | + } catch (UnknownHostException e) { | ||
143 | + log.error("[获取SDP IP]: 域名解析失败"); | ||
144 | + } | ||
145 | + return hostAddress; | ||
146 | + } | ||
147 | + } | ||
148 | + } | ||
149 | + | ||
150 | + public String getStreamIp() { | ||
151 | + if (ObjectUtils.isEmpty(streamIp)) { | ||
152 | + return ip; | ||
153 | + } else { | ||
154 | + return streamIp; | ||
155 | + } | ||
156 | + } | ||
157 | + | ||
158 | + private boolean isValidIPAddress(String ipAddress) { | ||
159 | + if ((ipAddress != null) && (!ipAddress.isEmpty())) { | ||
160 | + return Pattern.matches( | ||
161 | + "^([1-9]|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])(\\.(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])){3}$", | ||
162 | + ipAddress); | ||
163 | + } | ||
164 | + return false; | ||
165 | + } | ||
166 | +} |
common/data/src/main/java/org/thingsboard/server/common/data/yunteng/config/media/SipConfig.java
0 → 100644
1 | +package org.thingsboard.server.common.data.yunteng.config.media; | ||
2 | + | ||
3 | +import lombok.Data; | ||
4 | +import org.springframework.boot.context.properties.ConfigurationProperties; | ||
5 | +import org.springframework.stereotype.Component; | ||
6 | + | ||
7 | +@ConfigurationProperties(prefix = "sip") | ||
8 | +@Component | ||
9 | +@Data | ||
10 | +public class SipConfig { | ||
11 | + private String ip; | ||
12 | + private String showIp; | ||
13 | + private Integer port; | ||
14 | + private String id; | ||
15 | + private String domain; | ||
16 | + private String password; | ||
17 | + Integer ptzSpeed = 50; | ||
18 | + Integer registerTimeInterval = 120; | ||
19 | + private boolean alarm; | ||
20 | + | ||
21 | + public String getShowIp() { | ||
22 | + if (this.showIp == null) { | ||
23 | + return this.ip; | ||
24 | + } | ||
25 | + return showIp; | ||
26 | + } | ||
27 | +} |
1 | +package org.thingsboard.server.common.data.yunteng.config.media; | ||
2 | + | ||
3 | +import lombok.Data; | ||
4 | +import org.springframework.beans.factory.annotation.Value; | ||
5 | +import org.springframework.stereotype.Component; | ||
6 | + | ||
7 | +@Component | ||
8 | +@Data | ||
9 | +public class ThingsKitVersionConfig { | ||
10 | + @Value("${thingskit.release.version}") | ||
11 | + private String releaseVersion; | ||
12 | + | ||
13 | + @Value("${thingskit.release.date}") | ||
14 | + private String releaseDate; | ||
15 | +} |
common/data/src/main/java/org/thingsboard/server/common/data/yunteng/config/media/UserSetting.java
0 → 100644
1 | +package org.thingsboard.server.common.data.yunteng.config.media; | ||
2 | + | ||
3 | +import lombok.Data; | ||
4 | +import org.springframework.boot.context.properties.ConfigurationProperties; | ||
5 | +import org.springframework.stereotype.Component; | ||
6 | + | ||
7 | +@ConfigurationProperties(prefix = "user-settings") | ||
8 | +@Component | ||
9 | +@Data | ||
10 | +public class UserSetting { | ||
11 | + private String serverId = "000000"; | ||
12 | + private Boolean seniorSdp = Boolean.FALSE; | ||
13 | + private Boolean pushAuthority = Boolean.TRUE; | ||
14 | + private Boolean recordSip = Boolean.TRUE; | ||
15 | + private Boolean recordPushLive = Boolean.TRUE; | ||
16 | + private String recordPath = null; | ||
17 | + private Boolean streamOnDemand = Boolean.TRUE; | ||
18 | + private Boolean sipUseSourceIpAsRemoteAddress = Boolean.FALSE; | ||
19 | + private Integer playTimeout = 18000; | ||
20 | +} |
1 | +package org.thingsboard.server.common.data.yunteng.config.media; | ||
2 | + | ||
3 | +import com.fasterxml.jackson.annotation.JsonProperty; | ||
4 | +import lombok.Data; | ||
5 | + | ||
6 | +/** ZLMediaKit流媒体配置文件,具体配置说明可以参考ZLMediaKit的config.ini文件 */ | ||
7 | +@Data | ||
8 | +public class ZLMediaKitServerConfig { | ||
9 | + @JsonProperty(value = "api.apiDebug") | ||
10 | + private String apiDebug; | ||
11 | + | ||
12 | + @JsonProperty(value = "api.secret") | ||
13 | + private String apiSecret; | ||
14 | + | ||
15 | + @JsonProperty(value = "api.snapRoot") | ||
16 | + private String apiSnapRoot; | ||
17 | + | ||
18 | + @JsonProperty(value = "api.defaultSnap") | ||
19 | + private String apiDefaultSnap; | ||
20 | + | ||
21 | + @JsonProperty(value = "ffmpeg.bin") | ||
22 | + private String ffmpegBin; | ||
23 | + | ||
24 | + @JsonProperty(value = "ffmpeg.cmd") | ||
25 | + private String ffmpegCmd; | ||
26 | + | ||
27 | + @JsonProperty(value = "ffmpeg.snap") | ||
28 | + private String ffmpegSnap; | ||
29 | + | ||
30 | + @JsonProperty(value = "ffmpeg.log") | ||
31 | + private String ffmpegLog; | ||
32 | + | ||
33 | + @JsonProperty(value = "ffmpeg.restart_sec") | ||
34 | + private String ffmpegRestartSec; | ||
35 | + | ||
36 | + @JsonProperty(value = "protocol.modify_stamp") | ||
37 | + private String protocolModifyStamp; | ||
38 | + | ||
39 | + @JsonProperty(value = "protocol.enable_audio") | ||
40 | + private String protocolEnableAudio; | ||
41 | + | ||
42 | + @JsonProperty(value = "protocol.add_mute_audio") | ||
43 | + private String protocolAddMuteAudio; | ||
44 | + | ||
45 | + @JsonProperty(value = "protocol.continue_push_ms") | ||
46 | + private String protocolContinuePushMs; | ||
47 | + | ||
48 | + @JsonProperty(value = "protocol.enable_hls") | ||
49 | + private String protocolEnableHls; | ||
50 | + | ||
51 | + @JsonProperty(value = "protocol.enable_mp4") | ||
52 | + private String protocolEnableMp4; | ||
53 | + | ||
54 | + @JsonProperty(value = "protocol.enable_rtsp") | ||
55 | + private String protocolEnableRtsp; | ||
56 | + | ||
57 | + @JsonProperty(value = "protocol.enable_rtmp") | ||
58 | + private String protocolEnableRtmp; | ||
59 | + | ||
60 | + @JsonProperty(value = "protocol.enable_ts") | ||
61 | + private String protocolEnableTs; | ||
62 | + | ||
63 | + @JsonProperty(value = "protocol.enable_fmp4") | ||
64 | + private String protocolEnableFmp4; | ||
65 | + | ||
66 | + @JsonProperty(value = "protocol.mp4_as_player") | ||
67 | + private String protocolMp4AsPlayer; | ||
68 | + | ||
69 | + @JsonProperty(value = "protocol.mp4_max_second") | ||
70 | + private String protocolMp4MaxSecond; | ||
71 | + | ||
72 | + @JsonProperty(value = "protocol.mp4_save_path") | ||
73 | + private String protocolMp4SavePath; | ||
74 | + | ||
75 | + @JsonProperty(value = "protocol.hls_save_path") | ||
76 | + private String protocolHlsSavePath; | ||
77 | + | ||
78 | + @JsonProperty(value = "protocol.hls_demand") | ||
79 | + private String protocolHlsDemand; | ||
80 | + | ||
81 | + @JsonProperty(value = "protocol.rtsp_demand") | ||
82 | + private String protocolRtspDemand; | ||
83 | + | ||
84 | + @JsonProperty(value = "protocol.rtmp_demand") | ||
85 | + private String protocolRtmpDemand; | ||
86 | + | ||
87 | + @JsonProperty(value = "protocol.ts_demand") | ||
88 | + private String protocolTsDemand; | ||
89 | + | ||
90 | + @JsonProperty(value = "protocol.fmp4_demand") | ||
91 | + private String protocolFmp4Demand; | ||
92 | + | ||
93 | + @JsonProperty(value = "general.enableVhost") | ||
94 | + private String generalEnableVhost; | ||
95 | + | ||
96 | + @JsonProperty(value = "general.flowThreshold") | ||
97 | + private String generalFlowThreshold; | ||
98 | + | ||
99 | + @JsonProperty(value = "general.maxStreamWaitMS") | ||
100 | + private String generalMaxStreamWaitMS; | ||
101 | + | ||
102 | + @JsonProperty(value = "general.streamNoneReaderDelayMS") | ||
103 | + private int generalStreamNoneReaderDelayMS; | ||
104 | + | ||
105 | + @JsonProperty(value = "general.resetWhenRePlay") | ||
106 | + private String generalResetWhenRePlay; | ||
107 | + | ||
108 | + @JsonProperty(value = "general.mergeWriteMS") | ||
109 | + private String generalMergeWriteMS; | ||
110 | + | ||
111 | + @JsonProperty(value = "general.mediaServerId") | ||
112 | + private String generalMediaServerId; | ||
113 | + | ||
114 | + @JsonProperty(value = "general.wait_track_ready_ms") | ||
115 | + private String generalWaitTrackReadyMs; | ||
116 | + | ||
117 | + @JsonProperty(value = "general.wait_add_track_ms") | ||
118 | + private String generalWaitAddTrackMs; | ||
119 | + | ||
120 | + @JsonProperty(value = "general.unready_frame_cache") | ||
121 | + private String generalUnreadyFrameCache; | ||
122 | + | ||
123 | + @JsonProperty(value = "hls.fileBufSize") | ||
124 | + private String hlsFileBufSize; | ||
125 | + | ||
126 | + @JsonProperty(value = "hls.filePath") | ||
127 | + private String hlsFilePath; | ||
128 | + | ||
129 | + @JsonProperty(value = "hls.segDur") | ||
130 | + private String hlsSegDur; | ||
131 | + | ||
132 | + @JsonProperty(value = "hls.segNum") | ||
133 | + private String hlsSegNum; | ||
134 | + | ||
135 | + @JsonProperty(value = "hls.segRetain") | ||
136 | + private String hlsSegRetain; | ||
137 | + | ||
138 | + @JsonProperty(value = "hls.broadcastRecordTs") | ||
139 | + private String hlsBroadcastRecordTs; | ||
140 | + | ||
141 | + @JsonProperty(value = "hls.deleteDelaySec") | ||
142 | + private String hlsDeleteDelaySec; | ||
143 | + | ||
144 | + @JsonProperty(value = "hls.segKeep") | ||
145 | + private String hlsSegKeep; | ||
146 | + | ||
147 | + @JsonProperty(value = "hook.access_file_except_hls") | ||
148 | + private String hookAccessFileExceptHLS; | ||
149 | + | ||
150 | + @JsonProperty(value = "hook.admin_params") | ||
151 | + private String hookAdminParams; | ||
152 | + | ||
153 | + @JsonProperty(value = "hook.alive_interval") | ||
154 | + private Float hookAliveInterval; | ||
155 | + | ||
156 | + @JsonProperty(value = "hook.enable") | ||
157 | + private String hookEnable; | ||
158 | + | ||
159 | + @JsonProperty(value = "hook.on_flow_report") | ||
160 | + private String hookOnFlowReport; | ||
161 | + | ||
162 | + @JsonProperty(value = "hook.on_http_access") | ||
163 | + private String hookOnHttpAccess; | ||
164 | + | ||
165 | + @JsonProperty(value = "hook.on_play") | ||
166 | + private String hookOnPlay; | ||
167 | + | ||
168 | + @JsonProperty(value = "hook.on_publish") | ||
169 | + private String hookOnPublish; | ||
170 | + | ||
171 | + @JsonProperty(value = "hook.on_record_mp4") | ||
172 | + private String hookOnRecordMp4; | ||
173 | + | ||
174 | + @JsonProperty(value = "hook.on_rtsp_auth") | ||
175 | + private String hookOnRtspAuth; | ||
176 | + | ||
177 | + @JsonProperty(value = "hook.on_rtsp_realm") | ||
178 | + private String hookOnRtspRealm; | ||
179 | + | ||
180 | + @JsonProperty(value = "hook.on_shell_login") | ||
181 | + private String hookOnShellLogin; | ||
182 | + | ||
183 | + @JsonProperty(value = "hook.on_stream_changed") | ||
184 | + private String hookOnStreamChanged; | ||
185 | + | ||
186 | + @JsonProperty(value = "hook.on_stream_none_reader") | ||
187 | + private String hookOnStreamNoneReader; | ||
188 | + | ||
189 | + @JsonProperty(value = "hook.on_stream_not_found") | ||
190 | + private String hookOnStreamNotFound; | ||
191 | + | ||
192 | + @JsonProperty(value = "hook.on_server_started") | ||
193 | + private String hookOnServerStarted; | ||
194 | + | ||
195 | + @JsonProperty(value = "hook.on_server_keepalive") | ||
196 | + private String hookOnServerKeepalive; | ||
197 | + | ||
198 | + @JsonProperty(value = "hook.on_send_rtp_stopped") | ||
199 | + private String hookOnSendRtpStopped; | ||
200 | + | ||
201 | + @JsonProperty(value = "hook.on_rtp_server_timeout") | ||
202 | + private String hookOnRtpServerTimeout; | ||
203 | + | ||
204 | + @JsonProperty(value = "hook.timeoutSec") | ||
205 | + private String hookTimeoutSec; | ||
206 | + | ||
207 | + @JsonProperty(value = "http.charSet") | ||
208 | + private String httpCharSet; | ||
209 | + | ||
210 | + @JsonProperty(value = "http.keepAliveSecond") | ||
211 | + private String httpKeepAliveSecond; | ||
212 | + | ||
213 | + @JsonProperty(value = "http.maxReqCount") | ||
214 | + private String httpMaxReqCount; | ||
215 | + | ||
216 | + @JsonProperty(value = "http.maxReqSize") | ||
217 | + private String httpMaxReqSize; | ||
218 | + | ||
219 | + @JsonProperty(value = "http.notFound") | ||
220 | + private String httpNotFound; | ||
221 | + | ||
222 | + @JsonProperty(value = "http.port") | ||
223 | + private int httpPort; | ||
224 | + | ||
225 | + @JsonProperty(value = "http.rootPath") | ||
226 | + private String httpRootPath; | ||
227 | + | ||
228 | + @JsonProperty(value = "http.sendBufSize") | ||
229 | + private String httpSendBufSize; | ||
230 | + | ||
231 | + @JsonProperty(value = "http.sslport") | ||
232 | + private int httpSslPort; | ||
233 | + | ||
234 | + @JsonProperty(value = "multicast.addrMax") | ||
235 | + private String multicastAddrMax; | ||
236 | + | ||
237 | + @JsonProperty(value = "multicast.addrMin") | ||
238 | + private String multicastAddrMin; | ||
239 | + | ||
240 | + @JsonProperty(value = "multicast.udpTTL") | ||
241 | + private String multicastUdpTTL; | ||
242 | + | ||
243 | + @JsonProperty(value = "record.appName") | ||
244 | + private String recordAppName; | ||
245 | + | ||
246 | + @JsonProperty(value = "record.filePath") | ||
247 | + private String recordFilePath; | ||
248 | + | ||
249 | + @JsonProperty(value = "record.fileSecond") | ||
250 | + private String recordFileSecond; | ||
251 | + | ||
252 | + @JsonProperty(value = "record.sampleMS") | ||
253 | + private String recordFileSampleMS; | ||
254 | + | ||
255 | + @JsonProperty(value = "rtmp.handshakeSecond") | ||
256 | + private String rtmpHandshakeSecond; | ||
257 | + | ||
258 | + @JsonProperty(value = "rtmp.keepAliveSecond") | ||
259 | + private String rtmpKeepAliveSecond; | ||
260 | + | ||
261 | + @JsonProperty(value = "rtmp.modifyStamp") | ||
262 | + private String rtmpModifyStamp; | ||
263 | + | ||
264 | + @JsonProperty(value = "rtmp.port") | ||
265 | + private int rtmpPort; | ||
266 | + | ||
267 | + @JsonProperty(value = "rtmp.sslport") | ||
268 | + private int rtmpSslPort; | ||
269 | + | ||
270 | + @JsonProperty(value = "rtp.audioMtuSize") | ||
271 | + private String rtpAudioMtuSize; | ||
272 | + | ||
273 | + @JsonProperty(value = "rtp.clearCount") | ||
274 | + private String rtpClearCount; | ||
275 | + | ||
276 | + @JsonProperty(value = "rtp.cycleMS") | ||
277 | + private String rtpCycleMS; | ||
278 | + | ||
279 | + @JsonProperty(value = "rtp.maxRtpCount") | ||
280 | + private String rtpMaxRtpCount; | ||
281 | + | ||
282 | + @JsonProperty(value = "rtp.videoMtuSize") | ||
283 | + private String rtpVideoMtuSize; | ||
284 | + | ||
285 | + @JsonProperty(value = "rtp_proxy.checkSource") | ||
286 | + private String rtpProxyCheckSource; | ||
287 | + | ||
288 | + @JsonProperty(value = "rtp_proxy.dumpDir") | ||
289 | + private String rtpProxyDumpDir; | ||
290 | + | ||
291 | + @JsonProperty(value = "rtp_proxy.port") | ||
292 | + private int rtpProxyPort; | ||
293 | + | ||
294 | + @JsonProperty(value = "rtp_proxy.port_range") | ||
295 | + private String portRange; | ||
296 | + | ||
297 | + @JsonProperty(value = "rtp_proxy.timeoutSec") | ||
298 | + private String rtpProxyTimeoutSec; | ||
299 | + | ||
300 | + @JsonProperty(value = "rtsp.authBasic") | ||
301 | + private String rtspAuthBasic; | ||
302 | + | ||
303 | + @JsonProperty(value = "rtsp.handshakeSecond") | ||
304 | + private String rtspHandshakeSecond; | ||
305 | + | ||
306 | + @JsonProperty(value = "rtsp.keepAliveSecond") | ||
307 | + private String rtspKeepAliveSecond; | ||
308 | + | ||
309 | + @JsonProperty(value = "rtsp.port") | ||
310 | + private int rtspPort; | ||
311 | + | ||
312 | + @JsonProperty(value = "rtsp.sslport") | ||
313 | + private int rtspSslPort; | ||
314 | + | ||
315 | + @JsonProperty(value = "shell.maxReqSize") | ||
316 | + private String shellMaxReqSize; | ||
317 | + | ||
318 | + @JsonProperty(value = "shell.port") | ||
319 | + private String shellPort; | ||
320 | + | ||
321 | + @JsonProperty(value = "ip") | ||
322 | + private String ip; | ||
323 | + | ||
324 | + private String sdpIp; | ||
325 | + | ||
326 | + private String streamIp; | ||
327 | + | ||
328 | + private String hookIp; | ||
329 | + private String tenantId; | ||
330 | +} |
@@ -51,7 +51,6 @@ public interface FastIotConstants { | @@ -51,7 +51,6 @@ public interface FastIotConstants { | ||
51 | * 操作功能码类型 | 51 | * 操作功能码类型 |
52 | */ | 52 | */ |
53 | String OPERATION_TYPE = "operationType"; | 53 | String OPERATION_TYPE = "operationType"; |
54 | - | ||
55 | /** | 54 | /** |
56 | * 缩放因子 | 55 | * 缩放因子 |
57 | */ | 56 | */ |
@@ -67,6 +66,146 @@ public interface FastIotConstants { | @@ -67,6 +66,146 @@ public interface FastIotConstants { | ||
67 | */ | 66 | */ |
68 | String BIT_MASK = "bitMask"; | 67 | String BIT_MASK = "bitMask"; |
69 | } | 68 | } |
69 | + interface CacheSipKey { | ||
70 | + String TK_SIP_CACHE_NAME = "tkSipCacheName"; | ||
71 | + String TK_OTHER_RECEIVE_RTP_INFO = "TK_OTHER_RECEIVE_RTP_INFO_"; | ||
72 | + | ||
73 | + String TK_OTHER_RECEIVE_PS_INFO = "TK_OTHER_RECEIVE_PS_INFO_"; | ||
74 | + } | ||
75 | + | ||
76 | + interface MediaServerKey { | ||
77 | + | ||
78 | + String MEDIA_SERVER_CACHE_NAME = "tkMediaServerCacheName"; | ||
79 | + String MEDIA_SERVER_PREFIX = "TK_MEDIA_SERVER_"; | ||
80 | + String MEDIA_TRANSACTION_USED_PREFIX = "TK_MEDIA_TRANSACTION_"; | ||
81 | + String MEDIA_SERVERS_ONLINE_PREFIX = "TK_MEDIA_ONLINE_SERVERS_"; | ||
82 | + String MEDIA_STREAM_AUTHORITY = "TK_MEDIA_STREAM_AUTHORITY_"; | ||
83 | + | ||
84 | + /** 点播信息的缓存键 */ | ||
85 | + String PLAYER_PREFIX = "TK_PLAYER"; | ||
86 | + | ||
87 | + /** 回放信息的缓存键 */ | ||
88 | + String PLAY_BACK_PREFIX = "TK_PLAY_BACK"; | ||
89 | + | ||
90 | + String TK_SERVER_STREAM_PREFIX = "TK_SIGNALLING_STREAM_"; | ||
91 | + | ||
92 | + String PLATFORM_SEND_RTP_INFO_PREFIX = "PLATFORM_SEND_RTP_INFO_"; | ||
93 | + } | ||
94 | + | ||
95 | + interface ZLMediaKitHttpApi { | ||
96 | + String BASE_URL = "/index/api"; | ||
97 | + | ||
98 | + /** 获取流列表,可选筛选参数 */ | ||
99 | + String GET_MEDIA_LIST = BASE_URL + "/getMediaList"; | ||
100 | + | ||
101 | + /** 获取rtp代理时的某路ssrc rtp信息 */ | ||
102 | + String GET_RTP_INFO = BASE_URL + "/getRtpInfo"; | ||
103 | + | ||
104 | + /** 获取视频流截图 */ | ||
105 | + String GET_STREAM_SNAP = BASE_URL + "/getSnap"; | ||
106 | + | ||
107 | + /** 通过fork FFmpeg进程的方式拉流代理,支持任意协议 */ | ||
108 | + String ADD_FFMPEG_SOURCE = BASE_URL + "/addFFmpegSource"; | ||
109 | + | ||
110 | + /** 关闭ffmpeg拉流代理(流注册成功后,也可以使用close_streams接口替代) */ | ||
111 | + String DEL_FFMPEG_SOURCE = BASE_URL + "/delFFmpegSource"; | ||
112 | + | ||
113 | + /** 获取服务器配置 */ | ||
114 | + String GET_SERVER_CONFIG = BASE_URL + "/getServerConfig"; | ||
115 | + | ||
116 | + /** 设置服务器配置 */ | ||
117 | + String SET_SERVER_CONFIG = BASE_URL + "/setServerConfig"; | ||
118 | + | ||
119 | + /** 创建GB28181 RTP接收端口,如果该端口接收数据超时,则会自动被回收(不用调用closeRtpServer接口) */ | ||
120 | + String OPEN_RTP_SERVER = BASE_URL + "/openRtpServer"; | ||
121 | + | ||
122 | + /** 关闭GB28181 RTP接收端口 */ | ||
123 | + String CLOSE_RTP_SERVER = BASE_URL + "/closeRtpServer"; | ||
124 | + | ||
125 | + /** | ||
126 | + * 作为GB28181客户端,启动ps-rtp推流,支持rtp/udp方式;该接口支持rtsp/rtmp等协议转ps-rtp推流。 | ||
127 | + * 第一次推流失败会直接返回错误,成功一次后,后续失败也将无限重试。 | ||
128 | + */ | ||
129 | + String START_SEND_RTP = BASE_URL + "/startSendRtp"; | ||
130 | + | ||
131 | + /** 停止GB28181 ps-rtp推流 */ | ||
132 | + String STOP_SEND_RTP = BASE_URL + "/stopSendRtp"; | ||
133 | + | ||
134 | + /** 重启服务器,只有Daemon方式才能重启,否则是直接关闭! */ | ||
135 | + String RESTART_SERVER = BASE_URL + "/restartServer"; | ||
136 | + } | ||
137 | + | ||
138 | + interface ZLMediaBody { | ||
139 | + /** 表单数据:异常码 */ | ||
140 | + String CODE = "code"; | ||
141 | + | ||
142 | + /** 表单数据:数据 */ | ||
143 | + String DATA = "data"; | ||
144 | + | ||
145 | + /** 表单数据:异常消息 */ | ||
146 | + String MSG = "msg"; | ||
147 | + | ||
148 | + /** 表单数据:流媒体密钥 */ | ||
149 | + String SECRET = "secret"; | ||
150 | + | ||
151 | + /** 流媒体服务器信息 */ | ||
152 | + String MEDIA = "media"; | ||
153 | + | ||
154 | + /** 流媒体服务器信息:ID */ | ||
155 | + String MEDIA_ID = "mediaId"; | ||
156 | + | ||
157 | + /** 流媒体服务器信息:会话描述协议 */ | ||
158 | + String MEDIA_SDP_IP = "sdpIp"; | ||
159 | + | ||
160 | + /** 数据流服务ID */ | ||
161 | + String SSRCINFO_STREAM = "streamId"; | ||
162 | + | ||
163 | + /** 数据流服务的端口 */ | ||
164 | + String SSRCINFO_PORT = "streamPort"; | ||
165 | + | ||
166 | + /** 同步源标识符 */ | ||
167 | + String SSRCINFO_SSRC = "ssrc"; | ||
168 | + | ||
169 | + /** 流媒体通道ID */ | ||
170 | + String CHANNEL_ID = "channelId"; | ||
171 | + | ||
172 | + /** 流媒体通道ID */ | ||
173 | + String CALL_ID = "callId"; | ||
174 | + | ||
175 | + /** 设备国标编号 */ | ||
176 | + String CAMERA_CODE = "cameraCode"; | ||
177 | + | ||
178 | + /** 方法类型 */ | ||
179 | + String METHOD_TYPE = "methodType"; | ||
180 | + | ||
181 | + /** 请求响应事件:头部标签内容 */ | ||
182 | + String MSG_HEADER = "msgHeader"; | ||
183 | + | ||
184 | + /** 请求响应事件:表单内容 */ | ||
185 | + String MSG_CONTEXT = "msgContext"; | ||
186 | + | ||
187 | + /** 设备控制内容 */ | ||
188 | + String CONTROL_CONTEXT = "ptzContext"; | ||
189 | + | ||
190 | + /** 订阅处理结果 */ | ||
191 | + String SESSION_TYPE = "sessionType"; | ||
192 | + | ||
193 | + /** 订阅处理结果 */ | ||
194 | + String SSRC_CHECK = "ssrcCheck"; | ||
195 | + } | ||
196 | + | ||
197 | + /** 视频流传输模式 */ | ||
198 | + interface StreamMode { | ||
199 | + /** tcp 被动模式 */ | ||
200 | + String TCP_PASSIVE = "TCP-PASSIVE"; | ||
201 | + | ||
202 | + /** tcp 主动模式 */ | ||
203 | + String TCP_ACTIVE = "TCP-ACTIVE"; | ||
204 | + | ||
205 | + /** udp */ | ||
206 | + String UDP = "UDP"; | ||
207 | + } | ||
208 | + | ||
70 | interface TBCacheConfig { | 209 | interface TBCacheConfig { |
71 | String TB_CACHE_CONFIG_KEY = "TB_CONNECT_CACHE"; | 210 | String TB_CACHE_CONFIG_KEY = "TB_CONNECT_CACHE"; |
72 | String EXISTING_TENANT = "EXISTING_TENANT"; | 211 | String EXISTING_TENANT = "EXISTING_TENANT"; |
@@ -248,4 +387,22 @@ public interface FastIotConstants { | @@ -248,4 +387,22 @@ public interface FastIotConstants { | ||
248 | public static String TK_MSG_EVENT_NODE = "org.thingsboard.rule.engine.yunteng.event.TkMsgEventNode"; | 387 | public static String TK_MSG_EVENT_NODE = "org.thingsboard.rule.engine.yunteng.event.TkMsgEventNode"; |
249 | } | 388 | } |
250 | 389 | ||
390 | + | ||
391 | + /** 设备扩展信息内容 */ | ||
392 | + interface DeviceAdditional { | ||
393 | + /** 摄像头信息 */ | ||
394 | + String SIP = "sip"; | ||
395 | + | ||
396 | + /** 设备图片 */ | ||
397 | + String AVATAR = "avatar"; | ||
398 | + | ||
399 | + /** 设备地理位置 */ | ||
400 | + String ADDRESS = "address"; | ||
401 | + | ||
402 | + /** 地理坐标之经度 */ | ||
403 | + String LONGITUDE = "longitude"; | ||
404 | + | ||
405 | + /** 地理坐标之维度 */ | ||
406 | + String LATITUDE = "latitude"; | ||
407 | + } | ||
251 | } | 408 | } |
@@ -133,6 +133,12 @@ public final class ModelConstants { | @@ -133,6 +133,12 @@ public final class ModelConstants { | ||
133 | 133 | ||
134 | /** 产品品类表 */ | 134 | /** 产品品类表 */ |
135 | public static final String TK_DEVICE_PROFILE_CATEGORY = "tk_device_profile_category"; | 135 | public static final String TK_DEVICE_PROFILE_CATEGORY = "tk_device_profile_category"; |
136 | + | ||
137 | + /** ZLMediaKit 流媒体表 */ | ||
138 | + public static final String TK_MEDIA_SERVER_NAME = "tk_media_server"; | ||
139 | + | ||
140 | + /** 视频通道表 */ | ||
141 | + public static final String TK_VIDEO_CHANNEL_NAME = "tk_video_channel"; | ||
136 | } | 142 | } |
137 | 143 | ||
138 | public static class TableFields { | 144 | public static class TableFields { |
1 | +package org.thingsboard.server.common.data.yunteng.constant; | ||
2 | + | ||
3 | +public class SipRequestMethodConstants { | ||
4 | + public static final String ACK = "ACK"; | ||
5 | + public static final String BYE = "BYE"; | ||
6 | + public static final String CANCEL = "CANCEL"; | ||
7 | + public static final String INVITE = "INVITE"; | ||
8 | + public static final String OPTIONS = "OPTIONS"; | ||
9 | + public static final String REGISTER = "REGISTER"; | ||
10 | + public static final String NOTIFY = "NOTIFY"; | ||
11 | + public static final String SUBSCRIBE = "SUBSCRIBE"; | ||
12 | + public static final String MESSAGE = "MESSAGE"; | ||
13 | + public static final String REFER = "REFER"; | ||
14 | + | ||
15 | + public static final String INFO = "INFO"; | ||
16 | + public static final String PRACK = "PRACK"; | ||
17 | + | ||
18 | + public static final String UPDATE = "UPDATE"; | ||
19 | + | ||
20 | + public static final String PUBLISH = "PUBLISH"; | ||
21 | +} |
@@ -120,7 +120,17 @@ public enum ErrorMessage { | @@ -120,7 +120,17 @@ public enum ErrorMessage { | ||
120 | EZVIZ_API_ERROR(400095,"荧石视频获取TokenAPI调用失败【%s】,错误码【%s】"), | 120 | EZVIZ_API_ERROR(400095,"荧石视频获取TokenAPI调用失败【%s】,错误码【%s】"), |
121 | EZVIZ_GET_URL_ERROR(400096,"荧石API调用获取URL失败!!"), | 121 | EZVIZ_GET_URL_ERROR(400096,"荧石API调用获取URL失败!!"), |
122 | IMPORT_TCP_ERROR(400097,"TCP产品不能导入INT,DOUBLE,BOOL,TEXT以外的数据类型属性!!"), | 122 | IMPORT_TCP_ERROR(400097,"TCP产品不能导入INT,DOUBLE,BOOL,TEXT以外的数据类型属性!!"), |
123 | - IMPORT_ERROR(400098,"请使用模板excel重新导入"), | 123 | + SSRC_INFO_NOT_FOUND(400098,"缓存事务信息未找到,设备【%s】 通道【%s】"), |
124 | + VIDEO_CHANNEL_NOT_FOUND(400099,"视频通道不存在"), | ||
125 | + ONLINE_MEDIA_SERVER_NOT_FOUND(400100,"没有可用的流媒体节点"), | ||
126 | + FOUND_VIDEO_DEVICE_FAILED(400101,"获取视频设备信息失败"), | ||
127 | + NOT_FOUND_MEDIA_SERVER_FOR_PLAY(400102,"未找到可用于播放的流媒体"), | ||
128 | + RECEIVE_STREAM_FAILED(400103,"开启收流失败"), | ||
129 | + GET_PLAY_PORT_FAILED(400104,"获取点播端口异常"), | ||
130 | + STREAM_INFO_NOT_FOUND_FOR_PLAY(400105,"点播信息未找到"), | ||
131 | + SIP_COMMAND_SEND_FAILED(400106,"sip命令下发失败"), | ||
132 | + NOT_BELONG_CURRENT_CUSTOMER(400107,"该数据不属于当前客户"), | ||
133 | + IMPORT_ERROR(400108,"请使用模板excel重新导入"), | ||
124 | HAVE_NO_PERMISSION(500002,"没有修改权限"), | 134 | HAVE_NO_PERMISSION(500002,"没有修改权限"), |
125 | NOT_ALLOED_ISOLATED_IN_MONOLITH(500003,"【monolith】模式下,不能选择【isolated】类型的租户配置"); | 135 | NOT_ALLOED_ISOLATED_IN_MONOLITH(500003,"【monolith】模式下,不能选择【isolated】类型的租户配置"); |
126 | 136 |
@@ -26,7 +26,6 @@ public class TkVideoDTO extends TenantDTO { | @@ -26,7 +26,6 @@ public class TkVideoDTO extends TenantDTO { | ||
26 | private String deviceType; | 26 | private String deviceType; |
27 | 27 | ||
28 | @ApiModelProperty(value = "摄像头编号/监控点位编号", required = true) | 28 | @ApiModelProperty(value = "摄像头编号/监控点位编号", required = true) |
29 | - @NotEmpty(message = "摄像头编号不能为空或空字符串") | ||
30 | private String sn; | 29 | private String sn; |
31 | 30 | ||
32 | @ApiModelProperty(value = "组织ID", required = true) | 31 | @ApiModelProperty(value = "组织ID", required = true) |
@@ -42,7 +41,7 @@ public class TkVideoDTO extends TenantDTO { | @@ -42,7 +41,7 @@ public class TkVideoDTO extends TenantDTO { | ||
42 | @ApiModelProperty(value = "摄像头描述") | 41 | @ApiModelProperty(value = "摄像头描述") |
43 | private String description; | 42 | private String description; |
44 | 43 | ||
45 | - @ApiModelProperty(value = "流获取方式:0 手动填写 1平台获取", required = true) | 44 | + @ApiModelProperty(value = "流获取方式:0 手动填写 1平台获取 2 GBT28281", required = true) |
46 | private Integer accessMode; | 45 | private Integer accessMode; |
47 | 46 | ||
48 | @ApiModelProperty(value = "平台ID") | 47 | @ApiModelProperty(value = "平台ID") |
common/data/src/main/java/org/thingsboard/server/common/data/yunteng/dto/TkVideoGptDTO.java
0 → 100644
1 | +package org.thingsboard.server.common.data.yunteng.dto; | ||
2 | + | ||
3 | +import com.fasterxml.jackson.databind.JsonNode; | ||
4 | +import io.swagger.annotations.ApiModelProperty; | ||
5 | +import lombok.Data; | ||
6 | +import lombok.EqualsAndHashCode; | ||
7 | + | ||
8 | +import javax.validation.constraints.NotEmpty; | ||
9 | +import java.util.List; | ||
10 | + | ||
11 | +@Data | ||
12 | +@EqualsAndHashCode(callSuper = true) | ||
13 | +public class TkVideoGptDTO extends TenantDTO { | ||
14 | + | ||
15 | + @ApiModelProperty(value = "组织ID", required = true) | ||
16 | + @NotEmpty(message = "组织ID不能为空或空字符串") | ||
17 | + private String organizationId; | ||
18 | + | ||
19 | + @ApiModelProperty(value = "流获取方式:0 手动填写 1平台获取 2 GBT28281", required = true) | ||
20 | + private Integer accessMode; | ||
21 | + | ||
22 | + @ApiModelProperty(value = "多个设备id与通道号") | ||
23 | + private List<TkVideoGptDeviceDTO> gptDeviceDTOS; | ||
24 | + | ||
25 | +} |
common/data/src/main/java/org/thingsboard/server/common/data/yunteng/dto/TkVideoGptDeviceDTO.java
0 → 100644
1 | +package org.thingsboard.server.common.data.yunteng.dto; | ||
2 | + | ||
3 | +import com.fasterxml.jackson.databind.JsonNode; | ||
4 | +import io.swagger.annotations.ApiModelProperty; | ||
5 | +import lombok.Data; | ||
6 | + | ||
7 | +import javax.validation.constraints.NotEmpty; | ||
8 | +import java.util.List; | ||
9 | + | ||
10 | +@Data | ||
11 | +public class TkVideoGptDeviceDTO { | ||
12 | + | ||
13 | + @ApiModelProperty(value = "设备id", required = true) | ||
14 | + @NotEmpty(message = "设备id不能为空") | ||
15 | + private String deviceID; | ||
16 | + | ||
17 | + @ApiModelProperty(value = "设备名称", required = true) | ||
18 | + @NotEmpty(message = "设备名称不能为空") | ||
19 | + private String deviceName; | ||
20 | + | ||
21 | + @ApiModelProperty(value = "通道号集合") | ||
22 | + @NotEmpty(message = "通道号集合不能为空") | ||
23 | + private List<String> channelNos; | ||
24 | + | ||
25 | +} |
common/data/src/main/java/org/thingsboard/server/common/data/yunteng/dto/sip/CatalogDataDTO.java
0 → 100644
1 | +package org.thingsboard.server.common.data.yunteng.dto.sip; | ||
2 | + | ||
3 | +import lombok.Data; | ||
4 | +import org.thingsboard.server.common.data.yunteng.enums.CatalogDataStatusEnum; | ||
5 | + | ||
6 | +import java.time.LocalDateTime; | ||
7 | +import java.util.List; | ||
8 | + | ||
9 | +@Data | ||
10 | +public class CatalogDataDTO { | ||
11 | + /** 命令序列号 */ | ||
12 | + private int sn; | ||
13 | + | ||
14 | + private int total; | ||
15 | + private List<VideoChanelDTO> channelList; | ||
16 | + private LocalDateTime lastTime; | ||
17 | + private SipDeviceDTO device; | ||
18 | + private String errorMsg; | ||
19 | + private CatalogDataStatusEnum status; | ||
20 | +} |
common/data/src/main/java/org/thingsboard/server/common/data/yunteng/dto/sip/MediaServerDTO.java
0 → 100644
1 | +package org.thingsboard.server.common.data.yunteng.dto.sip; | ||
2 | + | ||
3 | +import io.swagger.annotations.ApiModelProperty; | ||
4 | +import lombok.Data; | ||
5 | +import lombok.EqualsAndHashCode; | ||
6 | +import org.springframework.util.ObjectUtils; | ||
7 | +import org.thingsboard.server.common.data.yunteng.config.media.ZLMediaKitServerConfig; | ||
8 | +import org.thingsboard.server.common.data.yunteng.dto.TenantDTO; | ||
9 | + | ||
10 | +@EqualsAndHashCode(callSuper = true) | ||
11 | +@Data | ||
12 | +public class MediaServerDTO extends TenantDTO { | ||
13 | + private static final long serialVersionUID = -2499846994830954839L; | ||
14 | + | ||
15 | + @ApiModelProperty(value = "流媒体的ID,非主键ID") | ||
16 | + private String mediaServerId; | ||
17 | + | ||
18 | + @ApiModelProperty(value = "流媒体的IP") | ||
19 | + private String ip; | ||
20 | + | ||
21 | + @ApiModelProperty(value = "hook使用的IP") | ||
22 | + private String hookIp; | ||
23 | + | ||
24 | + @ApiModelProperty(value = "SDP IP") | ||
25 | + private String sdpIp; | ||
26 | + | ||
27 | + @ApiModelProperty(value = "流IP") | ||
28 | + private String streamIp; | ||
29 | + | ||
30 | + @ApiModelProperty(value = "HTTP端口") | ||
31 | + private Integer httpPort; | ||
32 | + | ||
33 | + @ApiModelProperty(value = "HTTPS端口") | ||
34 | + private Integer httpSslPort; | ||
35 | + | ||
36 | + @ApiModelProperty(value = "RTMP端口") | ||
37 | + private Integer rtmpPort; | ||
38 | + | ||
39 | + @ApiModelProperty(value = "RTMP安全协议端口") | ||
40 | + private Integer rtmpSslPort; | ||
41 | + | ||
42 | + @ApiModelProperty(value = "RTP收流端口(单端口模式有用)") | ||
43 | + private Integer rtpProxyPort; | ||
44 | + | ||
45 | + @ApiModelProperty(value = "RTSP端口") | ||
46 | + private Integer rtspPort; | ||
47 | + | ||
48 | + @ApiModelProperty(value = "RTSP安全协议端口") | ||
49 | + private Integer rtspSslPort; | ||
50 | + | ||
51 | + @ApiModelProperty(value = "是否开启自动配置ZLMediaKit") | ||
52 | + private boolean autoConfig; | ||
53 | + | ||
54 | + @ApiModelProperty(value = "ZLMediaKit鉴权参数") | ||
55 | + private String secret; | ||
56 | + | ||
57 | + @ApiModelProperty(value = "keepalive hook触发间隔,单位秒") | ||
58 | + private Float hookAliveInterval; | ||
59 | + | ||
60 | + @ApiModelProperty(value = "是否使用多端口模式") | ||
61 | + private boolean rtpEnable; | ||
62 | + | ||
63 | + @ApiModelProperty(value = "流媒体服务器的状态") | ||
64 | + private boolean status; | ||
65 | + | ||
66 | + @ApiModelProperty(value = "多端口RTP收流端口范围") | ||
67 | + private String rtpPortRange; | ||
68 | + | ||
69 | + @ApiModelProperty(value = "RTP发流端口范围") | ||
70 | + private String sendRtpPortRange; | ||
71 | + | ||
72 | + @ApiModelProperty(value = "assist服务端口") | ||
73 | + private Integer recordAssistPort; | ||
74 | + | ||
75 | + @ApiModelProperty(value = "最后一次心跳时间") | ||
76 | + private String lastKeepaliveTime; | ||
77 | + | ||
78 | + @ApiModelProperty(value = "是否是默认ZLM") | ||
79 | + private boolean defaultServer; | ||
80 | + | ||
81 | + @ApiModelProperty(value = "当前使用到的端口") | ||
82 | + private Integer currentPort; | ||
83 | + | ||
84 | + public MediaServerDTO(){ | ||
85 | + | ||
86 | + } | ||
87 | + public MediaServerDTO(ZLMediaKitServerConfig zlmServerConfig, String sipIp) { | ||
88 | + setId(zlmServerConfig.getGeneralMediaServerId()); | ||
89 | + ip = zlmServerConfig.getIp(); | ||
90 | + hookIp = ObjectUtils.isEmpty(zlmServerConfig.getHookIp()) ? sipIp : zlmServerConfig.getHookIp(); | ||
91 | + sdpIp = | ||
92 | + ObjectUtils.isEmpty(zlmServerConfig.getSdpIp()) | ||
93 | + ? zlmServerConfig.getIp() | ||
94 | + : zlmServerConfig.getSdpIp(); | ||
95 | + streamIp = | ||
96 | + ObjectUtils.isEmpty(zlmServerConfig.getStreamIp()) | ||
97 | + ? zlmServerConfig.getIp() | ||
98 | + : zlmServerConfig.getStreamIp(); | ||
99 | + httpPort = zlmServerConfig.getHttpPort(); | ||
100 | + httpSslPort = zlmServerConfig.getHttpSslPort(); | ||
101 | + rtmpPort = zlmServerConfig.getRtmpPort(); | ||
102 | + rtmpSslPort = zlmServerConfig.getRtmpSslPort(); | ||
103 | + rtpProxyPort = zlmServerConfig.getRtpProxyPort(); | ||
104 | + rtspPort = zlmServerConfig.getRtspPort(); | ||
105 | + rtspSslPort = zlmServerConfig.getRtspSslPort(); | ||
106 | + autoConfig = true; // 默认值true; | ||
107 | + secret = zlmServerConfig.getApiSecret(); | ||
108 | + hookAliveInterval = zlmServerConfig.getHookAliveInterval(); | ||
109 | + rtpEnable = false; // 默认使用单端口;直到用户自己设置开启多端口 | ||
110 | + rtpPortRange = zlmServerConfig.getPortRange().replace("_", ","); // 默认使用30000,30500作为级联时发送流的端口号 | ||
111 | + recordAssistPort = 0; // 默认关闭 | ||
112 | + } | ||
113 | +} |
common/data/src/main/java/org/thingsboard/server/common/data/yunteng/dto/sip/PTZCmdDTO.java
0 → 100644
1 | +package org.thingsboard.server.common.data.yunteng.dto.sip; | ||
2 | + | ||
3 | +import io.swagger.annotations.ApiModelProperty; | ||
4 | +import lombok.Data; | ||
5 | +import org.thingsboard.server.common.data.yunteng.enums.PTZCommandEnum; | ||
6 | + | ||
7 | +import java.io.Serializable; | ||
8 | + | ||
9 | +@Data | ||
10 | +public class PTZCmdDTO implements Serializable { | ||
11 | + | ||
12 | + @ApiModelProperty(value = "控制指令") | ||
13 | + private PTZCommandEnum command; | ||
14 | + | ||
15 | + @ApiModelProperty(value = "水平速度") | ||
16 | + private int horizonSpeed; | ||
17 | + | ||
18 | + @ApiModelProperty(value = "垂直速度") | ||
19 | + private int verticalSpeed; | ||
20 | + | ||
21 | + @ApiModelProperty(value = "缩放速度") | ||
22 | + private int zoomSpeed; | ||
23 | +} |
common/data/src/main/java/org/thingsboard/server/common/data/yunteng/dto/sip/SendRtpItemDTO.java
0 → 100644
1 | +package org.thingsboard.server.common.data.yunteng.dto.sip; | ||
2 | + | ||
3 | +import io.swagger.annotations.ApiModelProperty; | ||
4 | +import lombok.Data; | ||
5 | +import org.thingsboard.server.common.data.yunteng.enums.InviteStreamTypeEnum; | ||
6 | + | ||
7 | +@Data | ||
8 | +public class SendRtpItemDTO { | ||
9 | + @ApiModelProperty(value = "推流ip") | ||
10 | + private String ip; | ||
11 | + | ||
12 | + @ApiModelProperty(value = "推流端口") | ||
13 | + private int port; | ||
14 | + | ||
15 | + @ApiModelProperty(value = "推流标识") | ||
16 | + private String ssrc; | ||
17 | + | ||
18 | + @ApiModelProperty(value = "平台id") | ||
19 | + private String platformId; | ||
20 | + | ||
21 | + @ApiModelProperty(value = "对应设备id") | ||
22 | + private String deviceId; | ||
23 | + | ||
24 | + @ApiModelProperty(value = "直播流的应用名") | ||
25 | + private String app; | ||
26 | + | ||
27 | + @ApiModelProperty(value = "通道id") | ||
28 | + private String channelId; | ||
29 | + | ||
30 | + @ApiModelProperty(value = "推流状态:0 等待设备推流上来 1 等待上级平台回复ack 2 推流中") | ||
31 | + private int status = 0; | ||
32 | + | ||
33 | + @ApiModelProperty(value = "设备推流的streamId") | ||
34 | + private String streamId; | ||
35 | + | ||
36 | + @ApiModelProperty(value = "是否为tcp") | ||
37 | + private boolean tcp; | ||
38 | + | ||
39 | + @ApiModelProperty(value = "是否为tcp主动模式") | ||
40 | + private boolean tcpActive; | ||
41 | + | ||
42 | + @ApiModelProperty(value = "自己推流使用的端口") | ||
43 | + private int localPort; | ||
44 | + | ||
45 | + @ApiModelProperty(value = "使用的流媒体") | ||
46 | + private String mediaServerId; | ||
47 | + | ||
48 | + @ApiModelProperty(value = "使用的服务的ID") | ||
49 | + private String serverId; | ||
50 | + | ||
51 | + @ApiModelProperty(value = "invite 的 callId") | ||
52 | + private String CallId; | ||
53 | + | ||
54 | + @ApiModelProperty(value = "invite 的 fromTag") | ||
55 | + private String fromTag; | ||
56 | + | ||
57 | + @ApiModelProperty(value = "invite 的 toTag") | ||
58 | + private String toTag; | ||
59 | + | ||
60 | + @ApiModelProperty(value = "发送时,rtp的pt(uint8_t),不传时默认为96") | ||
61 | + private int pt = 96; | ||
62 | + | ||
63 | + @ApiModelProperty(value = "发送时,rtp的负载类型。为true时,负载为ps;为false时,为es") | ||
64 | + private boolean usePs = true; | ||
65 | + | ||
66 | + @ApiModelProperty(value = "当usePs 为false时,有效。为1时,发送音频;为0时,发送视频;不传时默认为0") | ||
67 | + private boolean onlyAudio = false; | ||
68 | + | ||
69 | + @ApiModelProperty(value = "是否开启rtcp保活") | ||
70 | + private boolean rtcp = false; | ||
71 | + | ||
72 | + @ApiModelProperty(value = "播放类型") | ||
73 | + private InviteStreamTypeEnum playType; | ||
74 | +} |
common/data/src/main/java/org/thingsboard/server/common/data/yunteng/dto/sip/SipDeviceDTO.java
0 → 100644
1 | +package org.thingsboard.server.common.data.yunteng.dto.sip; | ||
2 | + | ||
3 | +import io.swagger.annotations.ApiModelProperty; | ||
4 | +import lombok.Data; | ||
5 | +import org.thingsboard.server.common.data.yunteng.constant.FastIotConstants; | ||
6 | + | ||
7 | +import java.io.Serializable; | ||
8 | + | ||
9 | +@Data | ||
10 | +public class SipDeviceDTO implements Serializable { | ||
11 | + @ApiModelProperty(value = "设备国标编号") | ||
12 | + private String cameraCode; | ||
13 | + | ||
14 | + @ApiModelProperty(value = "wan地址") | ||
15 | + private String hostAddress; | ||
16 | + | ||
17 | + @ApiModelProperty(value = "设备访问平台的IP") | ||
18 | + private String localIp; | ||
19 | + | ||
20 | + @ApiModelProperty(value = "传输协议:UDP/TCP") | ||
21 | + private String transport; | ||
22 | + | ||
23 | + @ApiModelProperty(value = "字符集") | ||
24 | + private String charset; | ||
25 | + | ||
26 | + @ApiModelProperty(value = "收流IP") | ||
27 | + private String sdpIp; | ||
28 | + | ||
29 | + @ApiModelProperty(value = "数据流传输模式:UDP / TCP-ACTIVE:tcp主动模式 / TCP-PASSIVE:tcp被动模式") | ||
30 | + private String streamMode; | ||
31 | + | ||
32 | + @ApiModelProperty(value = "通道个数") | ||
33 | + private Integer channelCount; | ||
34 | + | ||
35 | + @ApiModelProperty(value = "设备使用的媒体id, 默认为null") | ||
36 | + private String mediaServerId; | ||
37 | + | ||
38 | + @ApiModelProperty(value = "是否开启ssrc校验,默认关闭,开启可以防止串流") | ||
39 | + private boolean ssrcCheck = true; | ||
40 | + @ApiModelProperty(value = "生产厂商") | ||
41 | + private String manufacturer; | ||
42 | + | ||
43 | + @ApiModelProperty(value = "设备名称") | ||
44 | + private String name; | ||
45 | + | ||
46 | + @ApiModelProperty(value = "设备型号") | ||
47 | + private String model; | ||
48 | + | ||
49 | + @ApiModelProperty(value = "固件版本") | ||
50 | + private String firmware; | ||
51 | + public Integer getStreamModeForParam() { | ||
52 | + if (FastIotConstants.StreamMode.UDP.equalsIgnoreCase(streamMode)) { | ||
53 | + return 0; | ||
54 | + }else if (FastIotConstants.StreamMode.TCP_PASSIVE.equalsIgnoreCase(streamMode)) { | ||
55 | + return 1; | ||
56 | + }else if (FastIotConstants.StreamMode.TCP_ACTIVE.equalsIgnoreCase(streamMode)) { | ||
57 | + return 2; | ||
58 | + } | ||
59 | + return 0; | ||
60 | + } | ||
61 | +} |
1 | +package org.thingsboard.server.common.data.yunteng.dto.sip; | ||
2 | + | ||
3 | +import lombok.Builder; | ||
4 | +import lombok.Data; | ||
5 | + | ||
6 | +import java.io.Serializable; | ||
7 | + | ||
8 | +@Data | ||
9 | +@Builder | ||
10 | +public class SipMessageHeaderDTO implements Serializable { | ||
11 | + private String callId; | ||
12 | + private String viaTag; | ||
13 | + private String fromTag; | ||
14 | + private String toTag; | ||
15 | + private String viaBranch; | ||
16 | +} |
common/data/src/main/java/org/thingsboard/server/common/data/yunteng/dto/sip/SsrcInfoDTO.java
0 → 100644
1 | +package org.thingsboard.server.common.data.yunteng.dto.sip; | ||
2 | + | ||
3 | +import lombok.Data; | ||
4 | + | ||
5 | +@Data | ||
6 | +public class SsrcInfoDTO { | ||
7 | + /**数据流服务的端口*/ | ||
8 | + private int port; | ||
9 | + /**同步源标识符*/ | ||
10 | + private String ssrc; | ||
11 | + /**数据流服务ID*/ | ||
12 | + private String stream; | ||
13 | + | ||
14 | + public SsrcInfoDTO(int rtpServerPort, String ssrc, String streamId) { | ||
15 | + this.port = rtpServerPort; | ||
16 | + this.ssrc = ssrc; | ||
17 | + stream = streamId; | ||
18 | + } | ||
19 | +} |
common/data/src/main/java/org/thingsboard/server/common/data/yunteng/dto/sip/SsrcTransactionDTO.java
0 → 100644
1 | +package org.thingsboard.server.common.data.yunteng.dto.sip; | ||
2 | + | ||
3 | +import lombok.Data; | ||
4 | +import org.thingsboard.server.common.data.yunteng.enums.SessionTypeEnum; | ||
5 | + | ||
6 | +import java.io.Serializable; | ||
7 | + | ||
8 | +/** | ||
9 | + * 视频流源信息 | ||
10 | + */ | ||
11 | +@Data | ||
12 | +public class SsrcTransactionDTO implements Serializable { | ||
13 | + private String cameraCode; | ||
14 | + private String channelId; | ||
15 | + private String callId; | ||
16 | + private String stream; | ||
17 | + private String mediaServerId; | ||
18 | + /**SSRC(Source Synchronization Control)是同步源标识符*/ | ||
19 | + private String ssrc; | ||
20 | + | ||
21 | + private SipMessageHeaderDTO sipTransactionInfo; | ||
22 | + | ||
23 | + private SessionTypeEnum type; | ||
24 | +} |
common/data/src/main/java/org/thingsboard/server/common/data/yunteng/dto/sip/StreamContentDTO.java
0 → 100644
1 | +package org.thingsboard.server.common.data.yunteng.dto.sip; | ||
2 | + | ||
3 | +import io.swagger.annotations.ApiModelProperty; | ||
4 | +import lombok.Data; | ||
5 | + | ||
6 | +/** | ||
7 | + * 视频流播放地址内容 | ||
8 | + */ | ||
9 | +@Data | ||
10 | +public class StreamContentDTO { | ||
11 | + @ApiModelProperty(value = "应用名") | ||
12 | + private String app; | ||
13 | + | ||
14 | + @ApiModelProperty(value = "流ID") | ||
15 | + private String stream; | ||
16 | + | ||
17 | + @ApiModelProperty(value = "IP") | ||
18 | + private String ip; | ||
19 | + | ||
20 | + @ApiModelProperty(value = "HTTP-FLV流地址") | ||
21 | + private String flv; | ||
22 | + | ||
23 | + @ApiModelProperty(value = "HTTPS-FLV流地址") | ||
24 | + private String https_flv; | ||
25 | + | ||
26 | + @ApiModelProperty(value = "Websocket-FLV流地址") | ||
27 | + private String ws_flv; | ||
28 | + | ||
29 | + @ApiModelProperty(value = "Websockets-FLV流地址") | ||
30 | + private String wss_flv; | ||
31 | + | ||
32 | + @ApiModelProperty(value = "HTTP-FMP4流地址") | ||
33 | + private String fmp4; | ||
34 | + | ||
35 | + @ApiModelProperty(value = "HTTPS-FMP4流地址") | ||
36 | + private String https_fmp4; | ||
37 | + | ||
38 | + @ApiModelProperty(value = "Websocket-FMP4流地址") | ||
39 | + private String ws_fmp4; | ||
40 | + | ||
41 | + @ApiModelProperty(value = "Websockets-FMP4流地址") | ||
42 | + private String wss_fmp4; | ||
43 | + | ||
44 | + @ApiModelProperty(value = "HLS流地址") | ||
45 | + private String hls; | ||
46 | + | ||
47 | + @ApiModelProperty(value = "HTTPS-HLS流地址") | ||
48 | + private String https_hls; | ||
49 | + | ||
50 | + @ApiModelProperty(value = "Websocket-HLS流地址") | ||
51 | + private String ws_hls; | ||
52 | + | ||
53 | + @ApiModelProperty(value = "Websockets-HLS流地址") | ||
54 | + private String wss_hls; | ||
55 | + | ||
56 | + @ApiModelProperty(value = "HTTP-TS流地址") | ||
57 | + private String ts; | ||
58 | + | ||
59 | + @ApiModelProperty(value = "HTTPS-TS流地址") | ||
60 | + private String https_ts; | ||
61 | + | ||
62 | + @ApiModelProperty(value = "Websocket-TS流地址") | ||
63 | + private String ws_ts; | ||
64 | + | ||
65 | + @ApiModelProperty(value = "Websockets-TS流地址") | ||
66 | + private String wss_ts; | ||
67 | + | ||
68 | + @ApiModelProperty(value = "RTMP流地址") | ||
69 | + private String rtmp; | ||
70 | + | ||
71 | + @ApiModelProperty(value = "RTMPS流地址") | ||
72 | + private String rtmps; | ||
73 | + | ||
74 | + @ApiModelProperty(value = "RTSP流地址") | ||
75 | + private String rtsp; | ||
76 | + | ||
77 | + @ApiModelProperty(value = "RTSPS流地址") | ||
78 | + private String rtsps; | ||
79 | + | ||
80 | + @ApiModelProperty(value = "RTC流地址") | ||
81 | + private String rtc; | ||
82 | + | ||
83 | + @ApiModelProperty(value = "RTCS流地址") | ||
84 | + private String rtcs; | ||
85 | + | ||
86 | + @ApiModelProperty(value = "流媒体ID") | ||
87 | + private String mediaServerId; | ||
88 | + | ||
89 | + @ApiModelProperty(value = "流编码信息") | ||
90 | + private Object tracks; | ||
91 | + | ||
92 | + @ApiModelProperty(value = "开始时间") | ||
93 | + private String startTime; | ||
94 | + | ||
95 | + @ApiModelProperty(value = "结束时间") | ||
96 | + private String endTime; | ||
97 | + | ||
98 | + private double progress; | ||
99 | + | ||
100 | + public StreamContentDTO(StreamInfoDTO streamInfo) { | ||
101 | + if (streamInfo == null) { | ||
102 | + return; | ||
103 | + } | ||
104 | + this.app = streamInfo.getApp(); | ||
105 | + this.stream = streamInfo.getStream(); | ||
106 | + if (streamInfo.getFlv() != null) { | ||
107 | + this.flv = streamInfo.getFlv().toString(); | ||
108 | + } | ||
109 | + if (streamInfo.getHttps_flv() != null) { | ||
110 | + this.https_flv = streamInfo.getHttps_flv().toString(); | ||
111 | + } | ||
112 | + if (streamInfo.getWs_flv() != null) { | ||
113 | + this.ws_flv = streamInfo.getWs_flv().toString(); | ||
114 | + } | ||
115 | + if (streamInfo.getWss_flv() != null) { | ||
116 | + this.wss_flv = streamInfo.getWss_flv().toString(); | ||
117 | + } | ||
118 | + if (streamInfo.getFmp4() != null) { | ||
119 | + this.fmp4 = streamInfo.getFmp4().toString(); | ||
120 | + } | ||
121 | + if (streamInfo.getHttps_fmp4() != null) { | ||
122 | + this.https_fmp4 = streamInfo.getHttps_fmp4().toString(); | ||
123 | + } | ||
124 | + if (streamInfo.getWs_fmp4() != null) { | ||
125 | + this.ws_fmp4 = streamInfo.getWs_fmp4().toString(); | ||
126 | + } | ||
127 | + if (streamInfo.getWss_fmp4() != null) { | ||
128 | + this.wss_fmp4 = streamInfo.getWss_fmp4().toString(); | ||
129 | + } | ||
130 | + if (streamInfo.getHls() != null) { | ||
131 | + this.hls = streamInfo.getHls().toString(); | ||
132 | + } | ||
133 | + if (streamInfo.getHttps_hls() != null) { | ||
134 | + this.https_hls = streamInfo.getHttps_hls().toString(); | ||
135 | + } | ||
136 | + if (streamInfo.getWs_hls() != null) { | ||
137 | + this.ws_hls = streamInfo.getWs_hls().toString(); | ||
138 | + } | ||
139 | + if (streamInfo.getWss_hls() != null) { | ||
140 | + this.wss_hls = streamInfo.getWss_hls().toString(); | ||
141 | + } | ||
142 | + if (streamInfo.getTs() != null) { | ||
143 | + this.ts = streamInfo.getTs().toString(); | ||
144 | + } | ||
145 | + if (streamInfo.getHttps_ts() != null) { | ||
146 | + this.https_ts = streamInfo.getHttps_ts().toString(); | ||
147 | + } | ||
148 | + if (streamInfo.getWs_ts() != null) { | ||
149 | + this.ws_ts = streamInfo.getWs_ts().toString(); | ||
150 | + } | ||
151 | + if (streamInfo.getRtmp() != null) { | ||
152 | + this.rtmp = streamInfo.getRtmp().toString(); | ||
153 | + } | ||
154 | + if (streamInfo.getRtmps() != null) { | ||
155 | + this.rtmps = streamInfo.getRtmps().toString(); | ||
156 | + } | ||
157 | + if (streamInfo.getRtsp() != null) { | ||
158 | + this.rtsp = streamInfo.getRtsp().toString(); | ||
159 | + } | ||
160 | + if (streamInfo.getRtsps() != null) { | ||
161 | + this.rtsps = streamInfo.getRtsps().toString(); | ||
162 | + } | ||
163 | + if (streamInfo.getRtc() != null) { | ||
164 | + this.rtc = streamInfo.getRtc().toString(); | ||
165 | + } | ||
166 | + if (streamInfo.getRtcs() != null) { | ||
167 | + this.rtcs = streamInfo.getRtcs().toString(); | ||
168 | + } | ||
169 | + | ||
170 | + this.mediaServerId = streamInfo.getMediaServerId(); | ||
171 | + this.tracks = streamInfo.getTracks(); | ||
172 | + this.startTime = streamInfo.getStartTime(); | ||
173 | + this.endTime = streamInfo.getEndTime(); | ||
174 | + this.progress = streamInfo.getProgress(); | ||
175 | + } | ||
176 | +} |
common/data/src/main/java/org/thingsboard/server/common/data/yunteng/dto/sip/StreamInfoDTO.java
0 → 100644
1 | +package org.thingsboard.server.common.data.yunteng.dto.sip; | ||
2 | + | ||
3 | +import io.swagger.annotations.ApiModelProperty; | ||
4 | +import lombok.Data; | ||
5 | + | ||
6 | +import java.io.Serializable; | ||
7 | +import java.util.Objects; | ||
8 | + | ||
9 | +/** | ||
10 | + * 点播的流信息,例如:播放地址 | ||
11 | + */ | ||
12 | +@Data | ||
13 | +public class StreamInfoDTO implements Serializable, Cloneable { | ||
14 | + @ApiModelProperty(value = "应用名") | ||
15 | + private String app; | ||
16 | + | ||
17 | + @ApiModelProperty(value = "流ID") | ||
18 | + private String stream; | ||
19 | + | ||
20 | + @ApiModelProperty(value = "设备编号") | ||
21 | + private String cameraCode; | ||
22 | + | ||
23 | + @ApiModelProperty(value = "通道编号") | ||
24 | + private String channelId; | ||
25 | + | ||
26 | + @ApiModelProperty(value = "IP") | ||
27 | + private String ip; | ||
28 | + | ||
29 | + @ApiModelProperty(value = "HTTP-FLV流地址") | ||
30 | + private StreamURLDTO flv; | ||
31 | + | ||
32 | + @ApiModelProperty(value = "HTTPS-FLV流地址") | ||
33 | + private StreamURLDTO https_flv; | ||
34 | + | ||
35 | + @ApiModelProperty(value = "Websocket-FLV流地址") | ||
36 | + private StreamURLDTO ws_flv; | ||
37 | + | ||
38 | + @ApiModelProperty(value = "Websockets-FLV流地址") | ||
39 | + private StreamURLDTO wss_flv; | ||
40 | + | ||
41 | + @ApiModelProperty(value = "HTTP-FMP4流地址") | ||
42 | + private StreamURLDTO fmp4; | ||
43 | + | ||
44 | + @ApiModelProperty(value = "HTTPS-FMP4流地址") | ||
45 | + private StreamURLDTO https_fmp4; | ||
46 | + | ||
47 | + @ApiModelProperty(value = "Websocket-FMP4流地址") | ||
48 | + private StreamURLDTO ws_fmp4; | ||
49 | + | ||
50 | + @ApiModelProperty(value = "Websockets-FMP4流地址") | ||
51 | + private StreamURLDTO wss_fmp4; | ||
52 | + | ||
53 | + @ApiModelProperty(value = "HLS流地址") | ||
54 | + private StreamURLDTO hls; | ||
55 | + | ||
56 | + @ApiModelProperty(value = "HTTPS-HLS流地址") | ||
57 | + private StreamURLDTO https_hls; | ||
58 | + | ||
59 | + @ApiModelProperty(value = "Websocket-HLS流地址") | ||
60 | + private StreamURLDTO ws_hls; | ||
61 | + | ||
62 | + @ApiModelProperty(value = "Websockets-HLS流地址") | ||
63 | + private StreamURLDTO wss_hls; | ||
64 | + | ||
65 | + @ApiModelProperty(value = "HTTP-TS流地址") | ||
66 | + private StreamURLDTO ts; | ||
67 | + | ||
68 | + @ApiModelProperty(value = "HTTPS-TS流地址") | ||
69 | + private StreamURLDTO https_ts; | ||
70 | + | ||
71 | + @ApiModelProperty(value = "Websocket-TS流地址") | ||
72 | + private StreamURLDTO ws_ts; | ||
73 | + | ||
74 | + @ApiModelProperty(value = "Websockets-TS流地址") | ||
75 | + private StreamURLDTO wss_ts; | ||
76 | + | ||
77 | + @ApiModelProperty(value = "RTMP流地址") | ||
78 | + private StreamURLDTO rtmp; | ||
79 | + | ||
80 | + @ApiModelProperty(value = "RTMPS流地址") | ||
81 | + private StreamURLDTO rtmps; | ||
82 | + | ||
83 | + @ApiModelProperty(value = "RTSP流地址") | ||
84 | + private StreamURLDTO rtsp; | ||
85 | + | ||
86 | + @ApiModelProperty(value = "RTSPS流地址") | ||
87 | + private StreamURLDTO rtsps; | ||
88 | + | ||
89 | + @ApiModelProperty(value = "RTC流地址") | ||
90 | + private StreamURLDTO rtc; | ||
91 | + | ||
92 | + @ApiModelProperty(value = "RTCS流地址") | ||
93 | + private StreamURLDTO rtcs; | ||
94 | + | ||
95 | + @ApiModelProperty(value = "流媒体ID") | ||
96 | + private String mediaServerId; | ||
97 | + | ||
98 | + @ApiModelProperty(value = "流编码信息") | ||
99 | + private Object tracks; | ||
100 | + | ||
101 | + @ApiModelProperty(value = "开始时间") | ||
102 | + private String startTime; | ||
103 | + | ||
104 | + @ApiModelProperty(value = "结束时间") | ||
105 | + private String endTime; | ||
106 | + | ||
107 | + @ApiModelProperty(value = "进度(录像下载使用)") | ||
108 | + private double progress; | ||
109 | + | ||
110 | + @ApiModelProperty(value = "是否暂停(录像回放使用)") | ||
111 | + private boolean pause; | ||
112 | + | ||
113 | + public void setRtmp(String host, int port, int sslPort, String app, String stream, String callIdParam) { | ||
114 | + String file = String.format("%s/%s%s", app, stream, callIdParam); | ||
115 | + if (port > 0) { | ||
116 | + this.rtmp = new StreamURLDTO("rtmp", host, port, file); | ||
117 | + } | ||
118 | + if (sslPort > 0) { | ||
119 | + this.rtmps = new StreamURLDTO("rtmps", host, sslPort, file); | ||
120 | + } | ||
121 | + } | ||
122 | + | ||
123 | + public void setRtsp(String host, int port, int sslPort, String app, String stream, String callIdParam) { | ||
124 | + String file = String.format("%s/%s%s", app, stream, callIdParam); | ||
125 | + if (port > 0) { | ||
126 | + this.rtsp = new StreamURLDTO("rtsp", host, port, file); | ||
127 | + } | ||
128 | + if (sslPort > 0) { | ||
129 | + this.rtsps = new StreamURLDTO("rtsps", host, sslPort, file); | ||
130 | + } | ||
131 | + } | ||
132 | + | ||
133 | + public void setFlv(String host, int port, int sslPort, String app, String stream, String callIdParam) { | ||
134 | + String file = String.format("%s/%s.live.flv%s", app, stream, callIdParam); | ||
135 | + if (port > 0) { | ||
136 | + this.flv = new StreamURLDTO("http", host, port, file); | ||
137 | + } | ||
138 | + this.ws_flv = new StreamURLDTO("ws", host, port, file); | ||
139 | + if (sslPort > 0) { | ||
140 | + this.https_flv = new StreamURLDTO("https", host, sslPort, file); | ||
141 | + this.wss_flv = new StreamURLDTO("wss", host, sslPort, file); | ||
142 | + } | ||
143 | + } | ||
144 | + | ||
145 | + public void setFmp4(String host, int port, int sslPort, String app, String stream, String callIdParam) { | ||
146 | + String file = String.format("%s/%s.live.mp4%s", app, stream, callIdParam); | ||
147 | + if (port > 0) { | ||
148 | + this.fmp4 = new StreamURLDTO("http", host, port, file); | ||
149 | + this.ws_fmp4 = new StreamURLDTO("ws", host, port, file); | ||
150 | + } | ||
151 | + if (sslPort > 0) { | ||
152 | + this.https_fmp4 = new StreamURLDTO("https", host, sslPort, file); | ||
153 | + this.wss_fmp4 = new StreamURLDTO("wss", host, sslPort, file); | ||
154 | + } | ||
155 | + } | ||
156 | + | ||
157 | + public void setHls(String host, int port, int sslPort, String app, String stream, String callIdParam) { | ||
158 | + String file = String.format("%s/%s/hls.m3u8%s", app, stream, callIdParam); | ||
159 | + if (port > 0) { | ||
160 | + this.hls = new StreamURLDTO("http", host, port, file); | ||
161 | + this.ws_hls = new StreamURLDTO("ws", host, port, file); | ||
162 | + } | ||
163 | + if (sslPort > 0) { | ||
164 | + this.https_hls = new StreamURLDTO("https", host, sslPort, file); | ||
165 | + this.wss_hls = new StreamURLDTO("wss", host, sslPort, file); | ||
166 | + } | ||
167 | + } | ||
168 | + | ||
169 | + public void setTs(String host, int port, int sslPort, String app, String stream, String callIdParam) { | ||
170 | + String file = String.format("%s/%s.live.ts%s", app, stream, callIdParam); | ||
171 | + | ||
172 | + if (port > 0) { | ||
173 | + this.ts = new StreamURLDTO("http", host, port, file); | ||
174 | + this.ws_ts = new StreamURLDTO("ws", host, port, file); | ||
175 | + } | ||
176 | + if (sslPort > 0) { | ||
177 | + this.https_ts = new StreamURLDTO("https", host, sslPort, file); | ||
178 | + this.wss_ts = new StreamURLDTO("wss", host, sslPort, file); | ||
179 | + } | ||
180 | + } | ||
181 | + | ||
182 | + public void setRtc(String host, int port, int sslPort, String app, String stream, String callIdParam) { | ||
183 | + if (callIdParam != null) { | ||
184 | + callIdParam = Objects.equals(callIdParam, "") ? callIdParam : callIdParam.replace("?", "&"); | ||
185 | + } | ||
186 | + String file = String.format("index/api/webrtc?app=%s&stream=%s&type=play%s", app, stream, callIdParam); | ||
187 | + if (port > 0) { | ||
188 | + this.rtc = new StreamURLDTO("http", host, port, file); | ||
189 | + } | ||
190 | + if (sslPort > 0) { | ||
191 | + this.rtcs = new StreamURLDTO("https", host, sslPort, file); | ||
192 | + } | ||
193 | + } | ||
194 | + | ||
195 | + @Override | ||
196 | + public StreamInfoDTO clone() { | ||
197 | + try { | ||
198 | + StreamInfoDTO clone = (StreamInfoDTO) super.clone(); | ||
199 | + // TODO: copy mutable state here, so the clone can't change the internals of the original | ||
200 | + return clone; | ||
201 | + } catch (CloneNotSupportedException e) { | ||
202 | + throw new AssertionError(); | ||
203 | + } | ||
204 | + } | ||
205 | +} |
common/data/src/main/java/org/thingsboard/server/common/data/yunteng/dto/sip/StreamURLDTO.java
0 → 100644
1 | +package org.thingsboard.server.common.data.yunteng.dto.sip; | ||
2 | + | ||
3 | +import io.swagger.annotations.ApiModel; | ||
4 | +import io.swagger.annotations.ApiModelProperty; | ||
5 | +import lombok.Data; | ||
6 | + | ||
7 | +import java.io.Serializable; | ||
8 | + | ||
9 | +@ApiModel(value = "流地址信息") | ||
10 | +@Data | ||
11 | +public class StreamURLDTO implements Serializable { | ||
12 | + @ApiModelProperty(value = "协议") | ||
13 | + private String protocol; | ||
14 | + | ||
15 | + @ApiModelProperty(value = "主机地址") | ||
16 | + private String host; | ||
17 | + | ||
18 | + @ApiModelProperty(value = "端口") | ||
19 | + private int port = -1; | ||
20 | + | ||
21 | + @ApiModelProperty(value = "定位位置") | ||
22 | + private String file; | ||
23 | + | ||
24 | + public StreamURLDTO() {} | ||
25 | + | ||
26 | + public StreamURLDTO(String protocol, String host, int port, String file) { | ||
27 | + this.protocol = protocol; | ||
28 | + this.host = host; | ||
29 | + this.port = port; | ||
30 | + this.file = file; | ||
31 | + } | ||
32 | + | ||
33 | + @Override | ||
34 | + public String toString() { | ||
35 | + if (protocol != null && host != null && port != -1) { | ||
36 | + return String.format("%s://%s:%s/%s", protocol, host, port, file); | ||
37 | + } else { | ||
38 | + return null; | ||
39 | + } | ||
40 | + } | ||
41 | +} |
common/data/src/main/java/org/thingsboard/server/common/data/yunteng/dto/sip/SyncStatusDTO.java
0 → 100644
1 | +package org.thingsboard.server.common.data.yunteng.dto.sip; | ||
2 | + | ||
3 | +import io.swagger.annotations.ApiModel; | ||
4 | +import io.swagger.annotations.ApiModelProperty; | ||
5 | +import lombok.Data; | ||
6 | + | ||
7 | +@ApiModel(value = "摄像机同步状态") | ||
8 | +@Data | ||
9 | +public class SyncStatusDTO { | ||
10 | + @ApiModelProperty(value = "总数") | ||
11 | + private int total; | ||
12 | + | ||
13 | + @ApiModelProperty(value = "当前更新多少") | ||
14 | + private int current; | ||
15 | + | ||
16 | + @ApiModelProperty(value = "错误描述") | ||
17 | + private String errorMsg; | ||
18 | + | ||
19 | + @ApiModelProperty(value = "是否同步中") | ||
20 | + private boolean syncIng; | ||
21 | +} |
common/data/src/main/java/org/thingsboard/server/common/data/yunteng/dto/sip/VideoChanelDTO.java
0 → 100644
1 | +package org.thingsboard.server.common.data.yunteng.dto.sip; | ||
2 | + | ||
3 | +import io.swagger.annotations.ApiModel; | ||
4 | +import io.swagger.annotations.ApiModelProperty; | ||
5 | +import java.time.LocalDateTime; | ||
6 | +import lombok.Data; | ||
7 | +import lombok.EqualsAndHashCode; | ||
8 | +import org.thingsboard.server.common.data.yunteng.dto.TenantDTO; | ||
9 | +import org.thingsboard.server.common.data.yunteng.enums.StatusEnum; | ||
10 | + | ||
11 | +@EqualsAndHashCode(callSuper = true) | ||
12 | +@ApiModel(value = "视频通道列表") | ||
13 | +@Data | ||
14 | +public class VideoChanelDTO extends TenantDTO { | ||
15 | + private static final long serialVersionUID = -7234375257329881606L; | ||
16 | + | ||
17 | + @ApiModelProperty(value = "视频通道名称") | ||
18 | + private String name; | ||
19 | + | ||
20 | + @ApiModelProperty(value = "视频通道ID") | ||
21 | + private String channelId; | ||
22 | + | ||
23 | + @ApiModelProperty(value = "设备国标编号") | ||
24 | + private String cameraCode; | ||
25 | + | ||
26 | + @ApiModelProperty(value = "平台设备主键") | ||
27 | + private String deviceId; | ||
28 | + | ||
29 | + @ApiModelProperty(value = "生产厂商") | ||
30 | + private String manufacturer; | ||
31 | + | ||
32 | + @ApiModelProperty(value = "型号") | ||
33 | + private String model; | ||
34 | + | ||
35 | + @ApiModelProperty(value = "设备归属") | ||
36 | + private String owner; | ||
37 | + | ||
38 | + @ApiModelProperty(value = "行政区域") | ||
39 | + private String civilCode; | ||
40 | + | ||
41 | + /** 警区 */ | ||
42 | + @ApiModelProperty("警区") | ||
43 | + private String block; | ||
44 | + | ||
45 | + /** 安装地址 */ | ||
46 | + @ApiModelProperty("安装地址") | ||
47 | + private String address; | ||
48 | + | ||
49 | + @ApiModelProperty(value = "是否有子设备:0没有 1有") | ||
50 | + private Integer parental; | ||
51 | + | ||
52 | + @ApiModelProperty(value = "信令安全模式缺省为0; 0:不采用;2:S/MIME 签名方式;3:S/\n" + "MIME加密签名同时采用方式;4:数字摘要方式") | ||
53 | + private Integer safetyWay; | ||
54 | + | ||
55 | + @ApiModelProperty( | ||
56 | + value = "注册方式缺省为1;1:符合IETFRFC3261标准的认证注册模\n" + "式;2:基于口令的双向认证注册模式;3:基于数字证书的双向认证注册模式") | ||
57 | + private Integer registerWay; | ||
58 | + | ||
59 | + @ApiModelProperty(value = "证书序列号") | ||
60 | + private String certNum; | ||
61 | + | ||
62 | + @ApiModelProperty(value = "证书有效标识(有证书的设备必选)缺省为0;证书有效标识:0:无效 1:有效") | ||
63 | + private Integer certifiable; | ||
64 | + | ||
65 | + @ApiModelProperty(value = "无效原因码") | ||
66 | + private Integer errorCode; | ||
67 | + | ||
68 | + @ApiModelProperty(value = "证书终止有效期") | ||
69 | + private LocalDateTime endTime; | ||
70 | + | ||
71 | + @ApiModelProperty(value = "保密属性缺省为0;0:不涉密,1:涉密") | ||
72 | + private Integer secrecy; | ||
73 | + | ||
74 | + @ApiModelProperty(value = "系统IP地址") | ||
75 | + private String ipAddress; | ||
76 | + | ||
77 | + @ApiModelProperty(value = "端口") | ||
78 | + private Integer port; | ||
79 | + | ||
80 | + @ApiModelProperty(value = "密码") | ||
81 | + private String password; | ||
82 | + | ||
83 | + @ApiModelProperty(value = "通道状态") | ||
84 | + private StatusEnum status; | ||
85 | + | ||
86 | + @ApiModelProperty(value = "云台类型") | ||
87 | + private int PTZType; | ||
88 | + | ||
89 | + @ApiModelProperty(value = "云台类型描述字符串") | ||
90 | + private String PTZTypeText; | ||
91 | + | ||
92 | + @ApiModelProperty(value = "经度") | ||
93 | + private double longitude; | ||
94 | + | ||
95 | + @ApiModelProperty(value = "纬度") | ||
96 | + private double latitude; | ||
97 | + | ||
98 | + @ApiModelProperty(value = "GCJ02坐标系经度") | ||
99 | + private double longitudeGcj02; | ||
100 | + | ||
101 | + @ApiModelProperty(value = "GCJ02坐标系纬度") | ||
102 | + private double latitudeGcj02; | ||
103 | + | ||
104 | + @ApiModelProperty(value = "WGS84坐标系经度") | ||
105 | + private double longitudeWgs84; | ||
106 | + | ||
107 | + @ApiModelProperty(value = "WGS84坐标系纬度") | ||
108 | + private double latitudeWgs84; | ||
109 | + | ||
110 | + @ApiModelProperty(value = "子设备数") | ||
111 | + private int subCount; | ||
112 | + | ||
113 | + @ApiModelProperty(value = "流唯一编号,存在表示正在直播") | ||
114 | + private String streamId; | ||
115 | + | ||
116 | + @ApiModelProperty(value = "是否含有音频") | ||
117 | + private boolean hasAudio; | ||
118 | + | ||
119 | + @ApiModelProperty(value = "标记通道的类型,0->国标通道 1->直播流通道 2->业务分组/虚拟组织/行政区划") | ||
120 | + private int channelType; | ||
121 | + | ||
122 | + public void setPTZType(int PTZType) { | ||
123 | + this.PTZType = PTZType; | ||
124 | + switch (PTZType) { | ||
125 | + case 0: | ||
126 | + this.PTZTypeText = "未知"; | ||
127 | + break; | ||
128 | + case 1: | ||
129 | + this.PTZTypeText = "球机"; | ||
130 | + break; | ||
131 | + case 2: | ||
132 | + this.PTZTypeText = "半球"; | ||
133 | + break; | ||
134 | + case 3: | ||
135 | + this.PTZTypeText = "固定枪机"; | ||
136 | + break; | ||
137 | + case 4: | ||
138 | + this.PTZTypeText = "遥控枪机"; | ||
139 | + break; | ||
140 | + } | ||
141 | + } | ||
142 | +} |
1 | +package org.thingsboard.server.common.data.yunteng.dto.sip.cache; | ||
2 | + | ||
3 | +import io.swagger.annotations.ApiModelProperty; | ||
4 | +import lombok.Data; | ||
5 | + | ||
6 | +import java.util.concurrent.atomic.AtomicInteger; | ||
7 | + | ||
8 | +@Data | ||
9 | +public class CacheMediaServerLoadDTO { | ||
10 | + @ApiModelProperty(value = "流媒体ID") | ||
11 | + private String key; | ||
12 | + | ||
13 | + @ApiModelProperty(value = "流媒体的负载值") | ||
14 | + private AtomicInteger load; | ||
15 | +} |
1 | +package org.thingsboard.server.common.data.yunteng.dto.sip.hook; | ||
2 | + | ||
3 | +import com.fasterxml.jackson.databind.node.ObjectNode; | ||
4 | +import org.thingsboard.server.common.data.yunteng.enums.HookTypeEnum; | ||
5 | + | ||
6 | +import java.time.LocalDateTime; | ||
7 | + | ||
8 | +/** | ||
9 | + * zlm hook事件的参数 | ||
10 | + * @author lin | ||
11 | + */ | ||
12 | +public interface IHookSubscribe { | ||
13 | + | ||
14 | + /** | ||
15 | + * 获取hook类型 | ||
16 | + * @return hook类型 | ||
17 | + */ | ||
18 | + HookTypeEnum getHookType(); | ||
19 | + | ||
20 | + /** | ||
21 | + * 获取hook的具体内容 | ||
22 | + * @return hook的具体内容 | ||
23 | + */ | ||
24 | + ObjectNode getContent(); | ||
25 | + | ||
26 | + /** | ||
27 | + * 设置过期时间 | ||
28 | + * @param expiresDateTime 过期时间 | ||
29 | + */ | ||
30 | + void setExpires(LocalDateTime expiresDateTime); | ||
31 | + | ||
32 | + /** | ||
33 | + * 获取过期时间 | ||
34 | + * @return 过期时间 | ||
35 | + */ | ||
36 | + LocalDateTime getExpires(); | ||
37 | +} |
1 | +package org.thingsboard.server.common.data.yunteng.dto.sip.hook; | ||
2 | + | ||
3 | +import lombok.Data; | ||
4 | + | ||
5 | +@Data | ||
6 | +public class OtherPsSendInfo { | ||
7 | + | ||
8 | + /** 发流IP */ | ||
9 | + private String sendLocalIp; | ||
10 | + | ||
11 | + /** 发流端口 */ | ||
12 | + private int sendLocalPort; | ||
13 | + | ||
14 | + /** 收流IP */ | ||
15 | + private String receiveIp; | ||
16 | + | ||
17 | + /** 收流端口 */ | ||
18 | + private int receivePort; | ||
19 | + | ||
20 | + /** 会话ID */ | ||
21 | + private String callId; | ||
22 | + | ||
23 | + /** 流ID */ | ||
24 | + private String stream; | ||
25 | + | ||
26 | + /** 推流应用名 */ | ||
27 | + private String pushApp; | ||
28 | + | ||
29 | + /** 推流流ID */ | ||
30 | + private String pushStream; | ||
31 | + | ||
32 | + /** 推流SSRC */ | ||
33 | + private String pushSSRC; | ||
34 | + | ||
35 | + @Override | ||
36 | + public String toString() { | ||
37 | + return "OtherPsSendInfo{" | ||
38 | + + "sendLocalIp='" | ||
39 | + + sendLocalIp | ||
40 | + + '\'' | ||
41 | + + ", sendLocalPort=" | ||
42 | + + sendLocalPort | ||
43 | + + ", receiveIp='" | ||
44 | + + receiveIp | ||
45 | + + '\'' | ||
46 | + + ", receivePort=" | ||
47 | + + receivePort | ||
48 | + + ", callId='" | ||
49 | + + callId | ||
50 | + + '\'' | ||
51 | + + ", stream='" | ||
52 | + + stream | ||
53 | + + '\'' | ||
54 | + + ", pushApp='" | ||
55 | + + pushApp | ||
56 | + + '\'' | ||
57 | + + ", pushStream='" | ||
58 | + + pushStream | ||
59 | + + '\'' | ||
60 | + + ", pushSSRC='" | ||
61 | + + pushSSRC | ||
62 | + + '\'' | ||
63 | + + '}'; | ||
64 | + } | ||
65 | +} |
1 | +package org.thingsboard.server.common.data.yunteng.dto.sip.hook; | ||
2 | + | ||
3 | +import lombok.Data; | ||
4 | + | ||
5 | +@Data | ||
6 | +public class OtherRtpSendInfo { | ||
7 | + | ||
8 | + /** 发流IP */ | ||
9 | + private String sendLocalIp; | ||
10 | + | ||
11 | + /** 音频发流端口 */ | ||
12 | + private int sendLocalPortForAudio; | ||
13 | + | ||
14 | + /** 视频发流端口 */ | ||
15 | + private int sendLocalPortForVideo; | ||
16 | + | ||
17 | + /** 收流IP */ | ||
18 | + private String receiveIp; | ||
19 | + | ||
20 | + /** 音频收流端口 */ | ||
21 | + private int receivePortForAudio; | ||
22 | + | ||
23 | + /** 视频收流端口 */ | ||
24 | + private int receivePortForVideo; | ||
25 | + | ||
26 | + /** 会话ID */ | ||
27 | + private String callId; | ||
28 | + | ||
29 | + /** 流ID */ | ||
30 | + private String stream; | ||
31 | + | ||
32 | + /** 推流应用名 */ | ||
33 | + private String pushApp; | ||
34 | + | ||
35 | + /** 推流流ID */ | ||
36 | + private String pushStream; | ||
37 | + | ||
38 | + /** 推流SSRC */ | ||
39 | + private String pushSSRC; | ||
40 | + | ||
41 | + @Override | ||
42 | + public String toString() { | ||
43 | + return "OtherRtpSendInfo{" | ||
44 | + + "sendLocalIp='" | ||
45 | + + sendLocalIp | ||
46 | + + '\'' | ||
47 | + + ", sendLocalPortForAudio=" | ||
48 | + + sendLocalPortForAudio | ||
49 | + + ", sendLocalPortForVideo=" | ||
50 | + + sendLocalPortForVideo | ||
51 | + + ", receiveIp='" | ||
52 | + + receiveIp | ||
53 | + + '\'' | ||
54 | + + ", receivePortForAudio=" | ||
55 | + + receivePortForAudio | ||
56 | + + ", receivePortForVideo=" | ||
57 | + + receivePortForVideo | ||
58 | + + ", callId='" | ||
59 | + + callId | ||
60 | + + '\'' | ||
61 | + + ", stream='" | ||
62 | + + stream | ||
63 | + + '\'' | ||
64 | + + ", pushApp='" | ||
65 | + + pushApp | ||
66 | + + '\'' | ||
67 | + + ", pushStream='" | ||
68 | + + pushStream | ||
69 | + + '\'' | ||
70 | + + ", pushSSRC='" | ||
71 | + + pushSSRC | ||
72 | + + '\'' | ||
73 | + + '}'; | ||
74 | + } | ||
75 | +} |
1 | +package org.thingsboard.server.common.data.yunteng.dto.sip.hook; | ||
2 | + | ||
3 | +import lombok.Data; | ||
4 | +import org.thingsboard.server.common.data.yunteng.dto.sip.hook.param.BaseParam; | ||
5 | +import org.thingsboard.server.common.data.yunteng.dto.sip.hook.param.OnStreamChangedHookParam; | ||
6 | + | ||
7 | +/** 流的鉴权信息 */ | ||
8 | +@Data | ||
9 | +public class StreamAuthorityInfo { | ||
10 | + | ||
11 | + private String id; | ||
12 | + private String app; | ||
13 | + private String stream; | ||
14 | + | ||
15 | + /** | ||
16 | + * 产生源类型, unknown = 0, rtmp_push=1, rtsp_push=2, rtp_push=3, pull=4, ffmpeg_pull=5, mp4_vod=6, | ||
17 | + * device_chn=7 | ||
18 | + */ | ||
19 | + private int originType; | ||
20 | + | ||
21 | + /** 产生源类型的字符串描述 */ | ||
22 | + private String originTypeStr; | ||
23 | + | ||
24 | + /** 推流时自定义的播放鉴权ID */ | ||
25 | + private String callId; | ||
26 | + | ||
27 | + /** 推流的鉴权签名 */ | ||
28 | + private String sign; | ||
29 | + | ||
30 | + public static StreamAuthorityInfo getInstanceByHook(BaseParam hookParam) { | ||
31 | + StreamAuthorityInfo streamAuthorityInfo = new StreamAuthorityInfo(); | ||
32 | + streamAuthorityInfo.setApp(hookParam.getApp()); | ||
33 | + streamAuthorityInfo.setStream(hookParam.getStream()); | ||
34 | + streamAuthorityInfo.setId(hookParam.getId()); | ||
35 | + return streamAuthorityInfo; | ||
36 | + } | ||
37 | + | ||
38 | + public static StreamAuthorityInfo getInstanceByHook( | ||
39 | + OnStreamChangedHookParam onStreamChangedHookParam) { | ||
40 | + StreamAuthorityInfo streamAuthorityInfo = new StreamAuthorityInfo(); | ||
41 | + streamAuthorityInfo.setApp(onStreamChangedHookParam.getApp()); | ||
42 | + streamAuthorityInfo.setStream(onStreamChangedHookParam.getStream()); | ||
43 | + streamAuthorityInfo.setId(onStreamChangedHookParam.getMediaServerId()); | ||
44 | + streamAuthorityInfo.setOriginType(onStreamChangedHookParam.getOriginType()); | ||
45 | + streamAuthorityInfo.setOriginTypeStr(onStreamChangedHookParam.getOriginTypeStr()); | ||
46 | + return streamAuthorityInfo; | ||
47 | + } | ||
48 | +} |
1 | +package org.thingsboard.server.common.data.yunteng.dto.sip.hook.param; | ||
2 | + | ||
3 | +import com.fasterxml.jackson.databind.node.ObjectNode; | ||
4 | +import lombok.Data; | ||
5 | +import org.thingsboard.server.common.data.yunteng.dto.sip.hook.IHookSubscribe; | ||
6 | +import org.thingsboard.server.common.data.yunteng.enums.HookTypeEnum; | ||
7 | + | ||
8 | +import java.time.LocalDateTime; | ||
9 | + | ||
10 | +@Data | ||
11 | +public class BaseHookSubscribe implements IHookSubscribe { | ||
12 | + protected HookTypeEnum hookType; | ||
13 | + protected ObjectNode content; | ||
14 | + protected LocalDateTime expires; | ||
15 | +} |
1 | +package org.thingsboard.server.common.data.yunteng.dto.sip.hook.param; | ||
2 | + | ||
3 | +import lombok.Data; | ||
4 | + | ||
5 | +@Data | ||
6 | +public class BaseParam { | ||
7 | + /** TCP链接唯一ID */ | ||
8 | + private String id; | ||
9 | + /** 流应用名 */ | ||
10 | + private String app; | ||
11 | + /** 流ID */ | ||
12 | + private String stream; | ||
13 | + /** 播放器ip */ | ||
14 | + private String ip; | ||
15 | + /** 播放url参数 */ | ||
16 | + private String params; | ||
17 | + /** 播放器端口号 */ | ||
18 | + private int port; | ||
19 | + /** 播放的协议: rtsp、rtmp、http */ | ||
20 | + private String schema; | ||
21 | + /** 流虚拟主机 */ | ||
22 | + private String vhost; | ||
23 | + /** 服务器id,通过配置文件设置 */ | ||
24 | + private String mediaServerId; | ||
25 | + | ||
26 | + @Override | ||
27 | + public String toString() { | ||
28 | + return String.format("%s://%s:%s/%s/%s?%s", schema, ip, port, app, stream, params); | ||
29 | + } | ||
30 | +} |
1 | +package org.thingsboard.server.common.data.yunteng.dto.sip.hook.param; | ||
2 | + | ||
3 | +import lombok.Data; | ||
4 | +import org.thingsboard.server.common.data.StringUtils; | ||
5 | + | ||
6 | +import java.io.Serializable; | ||
7 | + | ||
8 | +@Data | ||
9 | +public class HookResult implements Serializable { | ||
10 | + | ||
11 | + /** 错误代码,0代表允许播放 */ | ||
12 | + private int code; | ||
13 | + /** 不允许播放时的错误提示 */ | ||
14 | + private String msg; | ||
15 | + | ||
16 | + public HookResult() {} | ||
17 | + | ||
18 | + public HookResult(int code, String msg) { | ||
19 | + this.code = code; | ||
20 | + this.msg = msg; | ||
21 | + } | ||
22 | + | ||
23 | + public static HookResult SUCCESS() { | ||
24 | + return new HookResult(0, "success"); | ||
25 | + } | ||
26 | + | ||
27 | + public static HookResult Fail(int code, String message) { | ||
28 | + if (StringUtils.isEmpty(message)) { | ||
29 | + message = "fail"; | ||
30 | + } | ||
31 | + return new HookResult(code, message); | ||
32 | + } | ||
33 | +} |
1 | +package org.thingsboard.server.common.data.yunteng.dto.sip.hook.param; | ||
2 | + | ||
3 | +import lombok.Data; | ||
4 | + | ||
5 | +@Data | ||
6 | +public class HookResultForOnPublish extends HookResult { | ||
7 | + | ||
8 | + /** 转协议时是否开启音频 */ | ||
9 | + private boolean enableAudio; | ||
10 | + /** 是否允许mp4录制 */ | ||
11 | + private boolean enableMp4; | ||
12 | + /** mp4录制切片大小,单位秒 */ | ||
13 | + private int mp4MaxSecond; | ||
14 | + /** mp4录制文件保存根目录,置空使用默认 */ | ||
15 | + private String mp4SavePath; | ||
16 | + | ||
17 | + public static HookResultForOnPublish SUCCESS() { | ||
18 | + return new HookResultForOnPublish(0, "success"); | ||
19 | + } | ||
20 | + | ||
21 | + public HookResultForOnPublish(int code, String msg) { | ||
22 | + setCode(code); | ||
23 | + setMsg(msg); | ||
24 | + } | ||
25 | + | ||
26 | + @Override | ||
27 | + public String toString() { | ||
28 | + return "HookResultForOnPublish{" | ||
29 | + + "enable_audio=" | ||
30 | + + enableAudio | ||
31 | + + ", enable_mp4=" | ||
32 | + + enableMp4 | ||
33 | + + ", mp4_max_second=" | ||
34 | + + mp4MaxSecond | ||
35 | + + ", mp4_save_path='" | ||
36 | + + mp4SavePath | ||
37 | + + '\'' | ||
38 | + + '}'; | ||
39 | + } | ||
40 | +} |
1 | +package org.thingsboard.server.common.data.yunteng.dto.sip.hook.param; | ||
2 | + | ||
3 | +import com.fasterxml.jackson.databind.node.ObjectNode; | ||
4 | +import lombok.Data; | ||
5 | +import lombok.EqualsAndHashCode; | ||
6 | +import org.thingsboard.server.common.data.yunteng.enums.HookTypeEnum; | ||
7 | +import org.thingsboard.server.common.data.yunteng.utils.JacksonUtil; | ||
8 | + | ||
9 | +/** hook订阅-收流超时 */ | ||
10 | +@EqualsAndHashCode(callSuper = true) | ||
11 | +@Data | ||
12 | +public class HookSubscribeForRtpServerTimeout extends BaseHookSubscribe { | ||
13 | + private HookTypeEnum hookType = HookTypeEnum.ON_RTP_SERVER_TIMEOUT; | ||
14 | + public HookSubscribeForRtpServerTimeout(String stream, String ssrc, String mediaServerId) { | ||
15 | + ObjectNode param = JacksonUtil.newObjectNode(); | ||
16 | + param.put("stream_id", stream); | ||
17 | + param.put("ssrc", ssrc); | ||
18 | + param.put("mediaServerId", mediaServerId); | ||
19 | + this.content = param; | ||
20 | + } | ||
21 | +} |
1 | +package org.thingsboard.server.common.data.yunteng.dto.sip.hook.param; | ||
2 | + | ||
3 | +import lombok.Data; | ||
4 | +import lombok.EqualsAndHashCode; | ||
5 | +import org.thingsboard.server.common.data.yunteng.enums.HookTypeEnum; | ||
6 | +import org.thingsboard.server.common.data.yunteng.utils.JacksonUtil; | ||
7 | + | ||
8 | +/** hook订阅-流变化 */ | ||
9 | +@EqualsAndHashCode(callSuper = true) | ||
10 | +@Data | ||
11 | +public class HookSubscribeForServerStarted extends BaseHookSubscribe { | ||
12 | + private HookTypeEnum hookType = HookTypeEnum.ON_SERVER_STARTED; | ||
13 | + | ||
14 | + public HookSubscribeForServerStarted() { | ||
15 | + this.content = JacksonUtil.newObjectNode(); | ||
16 | + } | ||
17 | +} |
1 | +package org.thingsboard.server.common.data.yunteng.dto.sip.hook.param; | ||
2 | + | ||
3 | +import com.fasterxml.jackson.databind.node.ObjectNode; | ||
4 | +import lombok.Data; | ||
5 | +import lombok.EqualsAndHashCode; | ||
6 | +import org.thingsboard.server.common.data.yunteng.enums.HookTypeEnum; | ||
7 | +import org.thingsboard.server.common.data.yunteng.utils.JacksonUtil; | ||
8 | + | ||
9 | +/** | ||
10 | + * 流改变时 | ||
11 | + */ | ||
12 | +@EqualsAndHashCode(callSuper = true) | ||
13 | +@Data | ||
14 | +public class HookSubscribeForStreamChange extends BaseHookSubscribe { | ||
15 | + | ||
16 | + private HookTypeEnum hookType = HookTypeEnum.ON_STREAM_CHANGED; | ||
17 | + | ||
18 | + public HookSubscribeForStreamChange( | ||
19 | + String app, String stream, boolean regist, String schema, String mediaServerId) { | ||
20 | + ObjectNode objectNode = JacksonUtil.newObjectNode(); | ||
21 | + objectNode.put("app", app); | ||
22 | + objectNode.put("stream", stream); | ||
23 | + objectNode.put("regist", regist); | ||
24 | + if (schema != null) { | ||
25 | + objectNode.put("schema", schema); | ||
26 | + } | ||
27 | + objectNode.put("mediaServerId", mediaServerId); | ||
28 | + this.content = objectNode; | ||
29 | + } | ||
30 | +} |
1 | +package org.thingsboard.server.common.data.yunteng.dto.sip.hook.param; | ||
2 | + | ||
3 | +import lombok.Data; | ||
4 | + | ||
5 | +/** | ||
6 | + * 调用openRtpServer 接口,rtp server 长时间未收到数据,执行此web hook,对回复不敏感 | ||
7 | + */ | ||
8 | +@Data | ||
9 | +public class OnRtpServerTimeoutHookParam extends HookParam { | ||
10 | + private int localPort; | ||
11 | + private String streamId; | ||
12 | + private int tcpMode; | ||
13 | + private boolean reUsePort; | ||
14 | + private String ssrc; | ||
15 | +} |
1 | +package org.thingsboard.server.common.data.yunteng.dto.sip.hook.param; | ||
2 | + | ||
3 | +import lombok.Data; | ||
4 | + | ||
5 | +/** zlm hook事件中的on_send_rtp_stopped事件的参数 */ | ||
6 | +@Data | ||
7 | +public class OnSendRtpStoppedHookParam extends HookParam { | ||
8 | + /** 流应用名 */ | ||
9 | + private String app; | ||
10 | + /** 流ID */ | ||
11 | + private String stream; | ||
12 | +} |
1 | +package org.thingsboard.server.common.data.yunteng.dto.sip.hook.param; | ||
2 | + | ||
3 | +import lombok.Data; | ||
4 | +import lombok.EqualsAndHashCode; | ||
5 | +import org.thingsboard.server.common.data.yunteng.dto.sip.StreamContentDTO; | ||
6 | + | ||
7 | +import java.util.List; | ||
8 | + | ||
9 | +/** rtsp/rtmp流注册或注销时触发此事件;此事件对回复不敏感 */ | ||
10 | +@EqualsAndHashCode(callSuper = true) | ||
11 | +@Data | ||
12 | +public class OnStreamChangedHookParam extends HookParam { | ||
13 | + | ||
14 | + /** 注册/注销 */ | ||
15 | + private boolean regist; | ||
16 | + | ||
17 | + /** 应用名 */ | ||
18 | + private String app; | ||
19 | + | ||
20 | + /** 流id */ | ||
21 | + private String stream; | ||
22 | + | ||
23 | + /** 推流鉴权Id */ | ||
24 | + private String callId; | ||
25 | + | ||
26 | + /** 观看总人数,包括hls/rtsp/rtmp/http-flv/ws-flv */ | ||
27 | + private String totalReaderCount; | ||
28 | + | ||
29 | + /** 协议 包括hls/rtsp/rtmp/http-flv/ws-flv */ | ||
30 | + private String schema; | ||
31 | + | ||
32 | + /** | ||
33 | + * 产生源类型, unknown = 0, rtmp_push=1, rtsp_push=2, rtp_push=3, pull=4, ffmpeg_pull=5, mp4_vod=6, | ||
34 | + * device_chn=7 | ||
35 | + */ | ||
36 | + private int originType; | ||
37 | + | ||
38 | + /** 客户端和服务器网络信息,可能为null类型 */ | ||
39 | + private OriginSock originSock; | ||
40 | + | ||
41 | + /** 产生源类型的字符串描述 */ | ||
42 | + private String originTypeStr; | ||
43 | + | ||
44 | + /** 产生源的url */ | ||
45 | + private String originUrl; | ||
46 | + | ||
47 | + /** 服务器id */ | ||
48 | + private String severId; | ||
49 | + | ||
50 | + /** GMT unix系统时间戳,单位秒 */ | ||
51 | + private Long createStamp; | ||
52 | + | ||
53 | + /** 存活时间,单位秒 */ | ||
54 | + private Long aliveSecond; | ||
55 | + | ||
56 | + /** 数据产生速度,单位byte/s */ | ||
57 | + private Long bytesSpeed; | ||
58 | + | ||
59 | + /** 音视频轨道 */ | ||
60 | + private List<MediaTrack> tracks; | ||
61 | + | ||
62 | + /** 音视频轨道 */ | ||
63 | + private String vhost; | ||
64 | + | ||
65 | + /** 是否是docker部署, docker部署不会自动更新zlm使用的端口,需要自己手动修改 */ | ||
66 | + private boolean docker; | ||
67 | + | ||
68 | + @Data | ||
69 | + public static class MediaTrack { | ||
70 | + /** 音频通道数 */ | ||
71 | + private int channels; | ||
72 | + | ||
73 | + /** H264 = 0, H265 = 1, AAC = 2, G711A = 3, G711U = 4 */ | ||
74 | + private int codecId; | ||
75 | + | ||
76 | + /** 编码类型名称 CodecAAC CodecH264 */ | ||
77 | + private String codecIdName; | ||
78 | + | ||
79 | + /** Video = 0, Audio = 1 */ | ||
80 | + private int codecType; | ||
81 | + | ||
82 | + /** 轨道是否准备就绪 */ | ||
83 | + private boolean ready; | ||
84 | + | ||
85 | + /** 音频采样位数 */ | ||
86 | + private int sampleBit; | ||
87 | + | ||
88 | + /** 音频采样率 */ | ||
89 | + private int sampleRate; | ||
90 | + | ||
91 | + /** 视频fps */ | ||
92 | + private int fps; | ||
93 | + | ||
94 | + /** 视频高 */ | ||
95 | + private int height; | ||
96 | + | ||
97 | + /** 视频宽 */ | ||
98 | + private int width; | ||
99 | + } | ||
100 | + | ||
101 | + @Data | ||
102 | + public static class OriginSock { | ||
103 | + private String identifier; | ||
104 | + private String local_ip; | ||
105 | + private int local_port; | ||
106 | + private String peer_ip; | ||
107 | + private int peer_port; | ||
108 | + } | ||
109 | + | ||
110 | + private StreamContentDTO streamInfo; | ||
111 | +} |
1 | +package org.thingsboard.server.common.data.yunteng.dto.sip.hook.param; | ||
2 | + | ||
3 | +/** | ||
4 | + * zlm hook事件中的on_stream_not_found事件的参数 | ||
5 | + * @author lin | ||
6 | + */ | ||
7 | +public class OnStreamNotFoundHookParam extends HookParam{ | ||
8 | + private String id; | ||
9 | + private String app; | ||
10 | + private String stream; | ||
11 | + private String ip; | ||
12 | + private String params; | ||
13 | + private int port; | ||
14 | + private String schema; | ||
15 | + private String vhost; | ||
16 | + | ||
17 | + | ||
18 | + public String getId() { | ||
19 | + return id; | ||
20 | + } | ||
21 | + | ||
22 | + public void setId(String id) { | ||
23 | + this.id = id; | ||
24 | + } | ||
25 | + | ||
26 | + public String getApp() { | ||
27 | + return app; | ||
28 | + } | ||
29 | + | ||
30 | + public void setApp(String app) { | ||
31 | + this.app = app; | ||
32 | + } | ||
33 | + | ||
34 | + public String getStream() { | ||
35 | + return stream; | ||
36 | + } | ||
37 | + | ||
38 | + public void setStream(String stream) { | ||
39 | + this.stream = stream; | ||
40 | + } | ||
41 | + | ||
42 | + public String getIp() { | ||
43 | + return ip; | ||
44 | + } | ||
45 | + | ||
46 | + public void setIp(String ip) { | ||
47 | + this.ip = ip; | ||
48 | + } | ||
49 | + | ||
50 | + public String getParams() { | ||
51 | + return params; | ||
52 | + } | ||
53 | + | ||
54 | + public void setParams(String params) { | ||
55 | + this.params = params; | ||
56 | + } | ||
57 | + | ||
58 | + public int getPort() { | ||
59 | + return port; | ||
60 | + } | ||
61 | + | ||
62 | + public void setPort(int port) { | ||
63 | + this.port = port; | ||
64 | + } | ||
65 | + | ||
66 | + public String getSchema() { | ||
67 | + return schema; | ||
68 | + } | ||
69 | + | ||
70 | + public void setSchema(String schema) { | ||
71 | + this.schema = schema; | ||
72 | + } | ||
73 | + | ||
74 | + public String getVhost() { | ||
75 | + return vhost; | ||
76 | + } | ||
77 | + | ||
78 | + public void setVhost(String vhost) { | ||
79 | + this.vhost = vhost; | ||
80 | + } | ||
81 | + | ||
82 | + @Override | ||
83 | + public String toString() { | ||
84 | + return String.format("%s://%s:%s/%s/%s?%s", schema, ip, port, app, stream, params); | ||
85 | + } | ||
86 | +} |
common/data/src/main/java/org/thingsboard/server/common/data/yunteng/enums/HookTypeEnum.java
0 → 100644
1 | +package org.thingsboard.server.common.data.yunteng.enums; | ||
2 | + | ||
3 | +public enum HookTypeEnum { | ||
4 | + /** 流量统计事件,播放器或推流器断开时并且耗用流量超过特定阈值时会触发此事件,阈值通过配置文件 */ | ||
5 | + ON_FLOW_REPORT, | ||
6 | + /** 访问http文件服务器上hls之外的文件时触发 */ | ||
7 | + ON_HTTP_ACCESS, | ||
8 | + /** | ||
9 | + * 播放器鉴权事件,rtsp/rtmp/http-flv/ws-flv/hls的播放都将触发此鉴权事件; | ||
10 | + * 如果流不存在,那么先触发on_play事件然后触发on_stream_not_found事件。 | ||
11 | + * 播放rtsp流时,如果该流启动了rtsp专属鉴权(on_rtsp_realm)那么将不再触发on_play事件 | ||
12 | + */ | ||
13 | + ON_PLAY, | ||
14 | + /** rtsp/rtmp/rtp推流鉴权事件 */ | ||
15 | + ON_PUBLISH, | ||
16 | + /** 录制mp4完成后通知事件;此事件对回复不敏感 */ | ||
17 | + ON_RECORD_MP4, | ||
18 | + /** rtsp专用的鉴权事件,先触发on_rtsp_realm事件然后才会触发on_rtsp_auth事件 */ | ||
19 | + ON_RTSP_AUTH, | ||
20 | + /** | ||
21 | + * 该rtsp流是否开启rtsp专用方式的鉴权事件,开启后才会触发on_rtsp_auth事件。 | ||
22 | + * | ||
23 | + * <p>需要指出的是rtsp也支持url参数鉴权,它支持两种方式鉴权 | ||
24 | + */ | ||
25 | + ON_RTSP_REALM, | ||
26 | + /** | ||
27 | + * shell登录鉴权,ZLMediaKit提供简单的telnet调试方式 | ||
28 | + * | ||
29 | + * <p>使用telnet 127.0.0.1 9000能进入MediaServer进程的shell界面 | ||
30 | + */ | ||
31 | + ON_SHELL_LOGIN, | ||
32 | + /** rtsp/rtmp流注册或注销时触发此事件;此事件对回复不敏感 */ | ||
33 | + ON_STREAM_CHANGED, | ||
34 | + /** | ||
35 | + * 流无人观看时事件,用户可以通过此事件选择是否关闭无人看的流。 | ||
36 | + * 一个直播流注册上线了,如果一直没人观看也会触发一次无人观看事件,触发时的协议schema是随机的,看哪种协议最晚注册(一般为hls)。 | ||
37 | + * 后续从有人观看转为无人观看,触发协议schema为最后一名观看者使用何种协议。 | ||
38 | + * 目前mp4/hls录制不当做观看人数(mp4录制可以通过配置文件mp4_as_player控制,但是rtsp/rtmp/rtp转推算观看人数,也会触发该事件 | ||
39 | + */ | ||
40 | + ON_STREAM_NONE_READER, | ||
41 | + /** 流未找到事件,用户可以在此事件触发时,立即去拉流,这样可以实现按需拉流;此事件对回复不敏感 */ | ||
42 | + ON_STREAM_NOT_FOUND, | ||
43 | + /** 服务器启动事件,可以用于监听服务器崩溃重启;此事件对回复不敏感 */ | ||
44 | + ON_SERVER_STARTED, | ||
45 | + /** 调用openRtpServer 接口,rtp server 长时间未收到数据,执行此web hook,对回复不敏感 */ | ||
46 | + ON_RTP_SERVER_TIMEOUT, | ||
47 | + /** 服务器定时上报时间,上报间隔可配置,默认10s上报一次 */ | ||
48 | + ON_SERVER_KEEPALIVE, | ||
49 | + /** 发送rtp(startSendRtp)被动关闭时回调 */ | ||
50 | + ON_SEND_RTP_STOPPED | ||
51 | +} |
common/data/src/main/java/org/thingsboard/server/common/data/yunteng/enums/OriginTypeEnum.java
0 → 100644
1 | +package org.thingsboard.server.common.data.yunteng.enums; | ||
2 | + | ||
3 | +public enum OriginTypeEnum { | ||
4 | + // 不可调整顺序 | ||
5 | + UNKNOWN("UNKNOWN"), | ||
6 | + RTMP_PUSH("PUSH"), | ||
7 | + RTSP_PUSH("PUSH"), | ||
8 | + RTP_PUSH("RTP"), | ||
9 | + PULL("PULL"), | ||
10 | + FFMPEG_PULL("PULL"), | ||
11 | + MP4_VOD("MP4_VOD"), | ||
12 | + DEVICE_CHN("DEVICE_CHN"), | ||
13 | + RTC_PUSH("PUSH"); | ||
14 | + | ||
15 | + private final String type; | ||
16 | + OriginTypeEnum(String type) { | ||
17 | + this.type = type; | ||
18 | + } | ||
19 | + | ||
20 | + public String getType() { | ||
21 | + return type; | ||
22 | + } | ||
23 | +} |
common/data/src/main/java/org/thingsboard/server/common/data/yunteng/enums/PTZCommandEnum.java
0 → 100644
1 | +package org.thingsboard.server.common.data.yunteng.enums; | ||
2 | + | ||
3 | +import lombok.Getter; | ||
4 | +import lombok.Setter; | ||
5 | + | ||
6 | +/** 云台命令枚举 */ | ||
7 | +@Getter | ||
8 | +public enum PTZCommandEnum { | ||
9 | + LEFT("LEFT", 2), | ||
10 | + RIGHT("RIGHT", 1), | ||
11 | + UP("UP", 8), | ||
12 | + DOWN("DOWN", 4), | ||
13 | + UP_LEFT("UP_LEFT", 10), | ||
14 | + UP_RIGHT("UP_RIGHT", 9), | ||
15 | + DOWN_LEFT("DOWN_LEFT", 6), | ||
16 | + DOWN_RIGHT("DOWN_RIGHT", 5), | ||
17 | + ZOOM_IN("ZOOM_IN", 16), | ||
18 | + ZOOM_OUT("ZOOM_OUT", 32), | ||
19 | + STOP("STOP", 0); | ||
20 | + | ||
21 | + private final String command; | ||
22 | + private final Integer cmdCode; | ||
23 | + | ||
24 | + PTZCommandEnum(String command, Integer cmdCode) { | ||
25 | + this.command = command; | ||
26 | + this.cmdCode = cmdCode; | ||
27 | + } | ||
28 | +} |
common/data/src/main/java/org/thingsboard/server/common/data/yunteng/enums/PTZTypeEnum.java
0 → 100644
1 | +package org.thingsboard.server.common.data.yunteng.enums; | ||
2 | + | ||
3 | +import lombok.Getter; | ||
4 | +import lombok.Setter; | ||
5 | + | ||
6 | +/** 云台类型 */ | ||
7 | +public enum PTZTypeEnum { | ||
8 | + UNKNOWN("未知", 0), | ||
9 | + BALL("球机", 1), | ||
10 | + HALFSPHERE("半球", 2), | ||
11 | + LOCK_GUNLOCK("固定枪机", 3), | ||
12 | + CONTROLL_GUNLOCK("遥控枪机", 4); | ||
13 | + | ||
14 | + @Getter @Setter private String name; | ||
15 | + @Getter @Setter private Integer index; | ||
16 | + | ||
17 | + PTZTypeEnum(String name, Integer index) { | ||
18 | + this.name = name; | ||
19 | + this.index = index; | ||
20 | + } | ||
21 | + | ||
22 | + public static PTZTypeEnum getItem(Integer index) { | ||
23 | + for (PTZTypeEnum item : values()) { | ||
24 | + if (item.index == index) { | ||
25 | + return item; | ||
26 | + } | ||
27 | + } | ||
28 | + return UNKNOWN; | ||
29 | + } | ||
30 | +} |
common/data/src/main/java/org/thingsboard/server/common/data/yunteng/enums/VideoCmdEnum.java
0 → 100644
1 | +package org.thingsboard.server.common.data.yunteng.enums; | ||
2 | + | ||
3 | +/** 视频流媒体平台指令类型:cmdType */ | ||
4 | +public enum VideoCmdEnum { | ||
5 | + Catalog, | ||
6 | + DeviceControl, | ||
7 | + Alarm, | ||
8 | + Keepalive, | ||
9 | + MediaStatus, | ||
10 | + MobilePosition, | ||
11 | + DeviceInfo, | ||
12 | + DeviceStatus, | ||
13 | + RecordInfo, | ||
14 | + Broadcast, | ||
15 | + ConfigDownload, | ||
16 | + DeviceConfig, | ||
17 | + PresetQuery | ||
18 | +} |