|
|
@@ -1,22 +1,25 @@
|
|
|
package org.jetlinks.community.media.controller;
|
|
|
|
|
|
+import cn.hutool.json.JSONObject;
|
|
|
+import com.google.common.collect.Maps;
|
|
|
import io.swagger.v3.oas.annotations.Operation;
|
|
|
import io.swagger.v3.oas.annotations.Parameter;
|
|
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
|
|
import lombok.AllArgsConstructor;
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
-import org.hswebframework.web.authorization.annotation.Authorize;
|
|
|
-import org.hswebframework.web.authorization.annotation.CreateAction;
|
|
|
-import org.hswebframework.web.authorization.annotation.Resource;
|
|
|
-import org.hswebframework.web.authorization.annotation.SaveAction;
|
|
|
+import org.hswebframework.web.authorization.annotation.*;
|
|
|
import org.hswebframework.web.crud.service.ReactiveCrudService;
|
|
|
import org.hswebframework.web.crud.web.reactive.ReactiveServiceCrudController;
|
|
|
import org.hswebframework.web.crud.web.reactive.ReactiveServiceQueryController;
|
|
|
import org.hswebframework.web.exception.BusinessException;
|
|
|
+import org.jetlinks.community.media.bean.StreamInfo;
|
|
|
import org.jetlinks.community.media.entity.MediaDevice;
|
|
|
import org.jetlinks.community.media.gb28181.result.PlayResult;
|
|
|
import org.jetlinks.community.media.gb28181.result.WVPResult;
|
|
|
+import org.jetlinks.community.media.service.LocalMediaDeviceChannelService;
|
|
|
import org.jetlinks.community.media.service.LocalMediaDeviceService;
|
|
|
+import org.jetlinks.community.media.service.LocalMediaServerItemService;
|
|
|
+import org.jetlinks.community.media.service.LocalPlayService;
|
|
|
import org.jetlinks.community.media.storage.impl.RedisCacheStorageImpl;
|
|
|
import org.jetlinks.community.media.transmit.callback.DeferredResultHolder;
|
|
|
import org.jetlinks.community.media.transmit.callback.RequestMessage;
|
|
|
@@ -25,8 +28,10 @@ import org.springframework.http.ResponseEntity;
|
|
|
import org.springframework.web.bind.annotation.*;
|
|
|
import org.springframework.web.context.request.async.DeferredResult;
|
|
|
import reactor.core.publisher.Mono;
|
|
|
+import reactor.util.function.Tuple2;
|
|
|
|
|
|
-import java.util.UUID;
|
|
|
+import java.util.*;
|
|
|
+import java.util.stream.Collectors;
|
|
|
|
|
|
/**
|
|
|
* @author lifang
|
|
|
@@ -44,16 +49,104 @@ import java.util.UUID;
|
|
|
@Tag(name = "媒体视频设备")
|
|
|
public class MediaDeviceController implements ReactiveServiceCrudController<MediaDevice, String>,DeferredController{
|
|
|
private final LocalMediaDeviceService mediaDeviceService;
|
|
|
-
|
|
|
private final RedisCacheStorageImpl redisCacheStorage;
|
|
|
private final DeferredResultHolder resultHolder;
|
|
|
private final SipCommander cmder;
|
|
|
+ private final LocalPlayService playService;
|
|
|
+ private final LocalMediaDeviceChannelService deviceChannelService;
|
|
|
+ private final LocalMediaServerItemService mediaServerItemService;
|
|
|
@Override
|
|
|
public ReactiveCrudService<MediaDevice, String> getService() {
|
|
|
return mediaDeviceService;
|
|
|
}
|
|
|
|
|
|
+ @QueryAction
|
|
|
+ @Operation(summary = "设备点播")
|
|
|
+ @PostMapping("/{deviceId}/{channelId}/_start")
|
|
|
+ public Mono<Object> play(@PathVariable String deviceId,
|
|
|
+ @PathVariable String channelId) {
|
|
|
+
|
|
|
+ return
|
|
|
+ //获取设备信息
|
|
|
+ mediaDeviceService.findById(deviceId)
|
|
|
+ //获取设备相连的媒体流服务器信息
|
|
|
+ .flatMap(playService::getNewMediaServerItem)
|
|
|
+ .switchIfEmpty(Mono.error(new BusinessException("未找到可用的zlm媒体服务器")))
|
|
|
+ .flatMap(mediaServerItem ->playService.play(mediaServerItem, deviceId, channelId, null, null))
|
|
|
+ .flatMap(this::deferredResultHandler);
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ @QueryAction
|
|
|
+ @Operation(summary = "停止点播")
|
|
|
+ @GetMapping("/{deviceId}/{channelId}/_stop")
|
|
|
+ public Mono<Object> playStop(@PathVariable String deviceId, @PathVariable String channelId) {
|
|
|
+ if(log.isDebugEnabled()){
|
|
|
+ log.debug(String.format("设备预览/回放停止API调用,streamId:%s_%s", deviceId, channelId ));
|
|
|
+ }
|
|
|
+
|
|
|
+ String uuid = cn.hutool.core.lang.UUID.randomUUID().toString();
|
|
|
+ DeferredResult<ResponseEntity<String>> result = new DeferredResult<>();
|
|
|
+ // 录像查询以channelId作为deviceId查询
|
|
|
+ String key = DeferredResultHolder.CALLBACK_CMD_STOP + deviceId + channelId;
|
|
|
+ resultHolder.put(key, uuid, result);
|
|
|
+ PlayResult<String> playResult = new PlayResult<String>();
|
|
|
+ playResult.setResult(result);
|
|
|
+ // 超时处理
|
|
|
+ playResult.onTimeout(Mono.fromRunnable(()->{
|
|
|
+ log.warn(String.format("设备预览/回放停止响应超时,deviceId/channelId:%s_%s ", deviceId, channelId));
|
|
|
+ RequestMessage msg = new RequestMessage();
|
|
|
+ msg.setId(uuid);
|
|
|
+ msg.setKey(key);
|
|
|
+ msg.setData("设备预览/回放停止响应超时");
|
|
|
+ resultHolder.invokeAllResult(msg);
|
|
|
+ }));
|
|
|
|
|
|
+ return Mono.justOrEmpty(redisCacheStorage.getDevice(deviceId))
|
|
|
+ .flatMap(device->
|
|
|
+ Mono.zip(cmder.streamByeCmd(deviceId, channelId, (event) -> {
|
|
|
+ StreamInfo streamInfo = redisCacheStorage.queryPlayByDevice(deviceId, channelId);
|
|
|
+ if (streamInfo == null) {
|
|
|
+ RequestMessage msg = new RequestMessage();
|
|
|
+ msg.setId(uuid);
|
|
|
+ msg.setKey(key);
|
|
|
+ msg.setData("点播未找到");
|
|
|
+ resultHolder.invokeAllResult(msg);
|
|
|
+ deviceChannelService.stopPlay(deviceId, channelId).subscribe();
|
|
|
+ }else {
|
|
|
+ redisCacheStorage.stopPlay(streamInfo);
|
|
|
+ deviceChannelService.stopPlay(streamInfo.getDeviceID(), streamInfo.getChannelId()).subscribe();
|
|
|
+ RequestMessage msg = new RequestMessage();
|
|
|
+ msg.setId(uuid);
|
|
|
+ msg.setKey(key);
|
|
|
+ msg.setData("success");
|
|
|
+ resultHolder.invokeAllResult(msg);
|
|
|
+ }
|
|
|
+ mediaServerItemService.closeRTPServer(device, channelId);
|
|
|
+ })
|
|
|
+ .mergeWith(Mono.fromRunnable(()->{
|
|
|
+ if (deviceId != null || channelId != null) {
|
|
|
+ JSONObject json = new JSONObject()
|
|
|
+ .putOpt("deviceId", deviceId)
|
|
|
+ .putOpt("channelId", channelId);
|
|
|
+ RequestMessage msg = new RequestMessage();
|
|
|
+ msg.setId(uuid);
|
|
|
+ msg.setKey(key);
|
|
|
+ msg.setData(json.toString());
|
|
|
+ resultHolder.invokeAllResult(msg);
|
|
|
+ } else {
|
|
|
+ log.warn("设备预览/回放停止API调用失败!");
|
|
|
+ RequestMessage msg = new RequestMessage();
|
|
|
+ msg.setId(uuid);
|
|
|
+ msg.setKey(key);
|
|
|
+ msg.setData("streamId null");
|
|
|
+ resultHolder.invokeAllResult(msg);
|
|
|
+ }
|
|
|
+ }))
|
|
|
+ .then(),Mono.just(deferredResultHandler(playResult)))
|
|
|
+ )
|
|
|
+ .flatMap(Tuple2::getT2);
|
|
|
+ }
|
|
|
|
|
|
@PostMapping("/{deviceId}/channels/_sync")
|
|
|
@CreateAction
|
|
|
@@ -70,7 +163,7 @@ public class MediaDeviceController implements ReactiveServiceCrudController<Medi
|
|
|
String key = DeferredResultHolder.CALLBACK_CMD_CATALOG + id;
|
|
|
String uuid = UUID.randomUUID().toString();
|
|
|
//默认超时时间为30分钟
|
|
|
- DeferredResult<ResponseEntity<MediaDevice>> result = new DeferredResult<>(30*60*1000L);
|
|
|
+ DeferredResult<ResponseEntity<MediaDevice>> result = new DeferredResult<>();
|
|
|
playResult.setResult(result);
|
|
|
playResult.onTimeout(Mono.fromRunnable(()->{
|
|
|
log.warn("设备[{}]通道信息同步超时", id);
|
|
|
@@ -81,8 +174,10 @@ public class MediaDeviceController implements ReactiveServiceCrudController<Medi
|
|
|
WVPResult<Object> wvpResult = new WVPResult<>();
|
|
|
wvpResult.setCode(-1);
|
|
|
wvpResult.setData(device);
|
|
|
- wvpResult.setMsg("更新超时");
|
|
|
+ wvpResult.setMsg("设备响应超时,请检查设备是否在线或网络是否通畅");
|
|
|
msg.setData(wvpResult);
|
|
|
+ //设备下线
|
|
|
+ mediaDeviceService.deviceOffline(device);
|
|
|
resultHolder.invokeAllResult(msg);
|
|
|
}));
|
|
|
// 等待其他相同请求返回时一起返回
|
|
|
@@ -101,6 +196,7 @@ public class MediaDeviceController implements ReactiveServiceCrudController<Medi
|
|
|
msg.setData(wvpResult);
|
|
|
resultHolder.invokeAllResult(msg);
|
|
|
})
|
|
|
+ .thenReturn(1L)
|
|
|
.flatMap(ignore->deferredResultHandler(playResult));
|
|
|
}
|
|
|
|