视频下载 添加 调用日志上报

This commit is contained in:
shikong 2024-03-26 15:08:56 +08:00
parent ddd13510a8
commit 5003b1268b
10 changed files with 157 additions and 4 deletions

View File

@ -0,0 +1,17 @@
package cn.skcks.docking.gb28181.wvp.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import java.util.HashMap;
import java.util.Map;
@Data
@Configuration
@ConfigurationProperties(prefix = "report")
public class ReportConfig {
private Boolean enabled = false;
private String url;
private Map<String, String> customHeaders = new HashMap<>();
}

View File

@ -0,0 +1,46 @@
package cn.skcks.docking.gb28181.wvp.dto.report;
import cn.hutool.core.date.DatePattern;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.format.annotation.DateTimeFormat;
import java.util.Date;
@AllArgsConstructor
@NoArgsConstructor
@Data
@Schema(description = "上报信息")
public class ReportReq {
@Schema(description = "上报消息id")
private String id;
@Schema(description = "设备编码")
private String deviceCode;
@Schema(description = "设备id/通道id")
private String deviceId;
@Schema(description = "点播时长")
private String durationTime;
@Schema(description = "点播时长")
private TimeRange timeRange;
@DateTimeFormat(pattern= DatePattern.NORM_DATETIME_PATTERN)
@JsonFormat(pattern = DatePattern.NORM_DATETIME_PATTERN)
@Schema(description = "日志记录时间")
private Date logTime;
@AllArgsConstructor
@NoArgsConstructor
@Data
public static class TimeRange {
@Schema(description = "开始时间")
private Date startTime;
@Schema(description = "结束时间")
private Date endTime;
}
@Schema(description = "文件大小, 未知大小为 -1")
private String fileSize;
}

View File

@ -231,7 +231,7 @@ public class Gb28181DownloadService {
writeErrorToResponse(asyncResponse, JsonResponse.error("下载失败"));
} else if (wvpProxyConfig.getUseFfmpeg()) {
log.info("开始 ffmpeg 录制, deviceCode {}, startTime {}, endTime {}", deviceCode, DateUtil.formatDateTime(startTime), DateUtil.formatDateTime(endTime));
videoService.ffmpegRecord(asyncResponse, videoInfo.getUrl(), DateUtil.between(startTime, endTime, DateUnit.SECOND), videoInfo.getDevice(), videoInfo.getCallId());
videoService.ffmpegRecord(request, asyncResponse, videoInfo.getUrl(), DateUtil.between(startTime, endTime, DateUnit.SECOND), videoInfo.getDevice(), videoInfo.getCallId());
DateTime end = DateUtil.date();
asyncContext.complete();
log.info("下载总耗时: {}, deviceCode {}, startTime {}, endTime {}", DateUtil.between(start, end, DateUnit.SECOND), deviceCode, DateUtil.formatDateTime(startTime), DateUtil.formatDateTime(endTime));
@ -376,7 +376,7 @@ public class Gb28181DownloadService {
writeErrorToResponse(asyncResponse, JsonResponse.error("下载失败"));
} else if(wvpProxyConfig.getUseFfmpeg()){
log.info("开始 ffmpeg 录制, deviceCode {}, startTime {}, endTime {}", deviceCode, DateUtil.formatDateTime(startTime), DateUtil.formatDateTime(endTime));
videoService.ffmpegRecord(asyncResponse, videoInfo.getUrl(), DateUtil.between(startTime, endTime, DateUnit.SECOND), videoInfo.getDevice(), videoInfo.getCallId());
videoService.ffmpegRecord(request, asyncResponse, videoInfo.getUrl(), DateUtil.between(startTime, endTime, DateUnit.SECOND), videoInfo.getDevice(), videoInfo.getCallId());
DateTime end = DateUtil.date();
asyncContext.complete();
log.info("下载总耗时: {}, deviceCode {}, startTime {}, endTime {}", DateUtil.between(start, end, DateUnit.SECOND), deviceCode, DateUtil.formatDateTime(startTime), DateUtil.formatDateTime(endTime));

View File

@ -0,0 +1,20 @@
package cn.skcks.docking.gb28181.wvp.service.report;
import cn.skcks.docking.gb28181.common.json.JsonResponse;
import cn.skcks.docking.gb28181.media.feign.IgnoreSSLFeignClientConfig;
import cn.skcks.docking.gb28181.wvp.dto.report.ReportReq;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
@FeignClient(
name = "ReportServiceProxy",
url = "${report.url}",
configuration = {IgnoreSSLFeignClientConfig.class}
)
public interface ReportApi {
@PostMapping
JsonResponse<?> report(@RequestHeader MultiValueMap<String, String> headers, @RequestBody ReportReq body);
}

View File

@ -0,0 +1,46 @@
package cn.skcks.docking.gb28181.wvp.service.report;
import cn.hutool.core.date.BetweenFormatter;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.unit.DataSizeUtil;
import cn.hutool.core.util.IdUtil;
import cn.skcks.docking.gb28181.wvp.config.ReportConfig;
import cn.skcks.docking.gb28181.wvp.dto.report.ReportReq;
import cn.skcks.docking.gb28181.wvp.orm.mybatis.dynamic.model.WvpProxyDevice;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap;
import java.util.Date;
@Slf4j
@Service
@RequiredArgsConstructor
public class ReportService {
private final ReportApi reportApi;
private final ReportConfig reportConfig;
public void report(HttpServletRequest request, WvpProxyDevice device, Date startTime, Date endTime, long fileSize) {
if(!reportConfig.getEnabled()){
return;
}
ReportReq reportReq = new ReportReq(IdUtil.fastUUID(),
device.getDeviceCode(),
device.getGbDeviceChannelId(),
DateUtil.formatBetween(startTime, endTime, BetweenFormatter.Level.SECOND),
new ReportReq.TimeRange(startTime, endTime),
DateUtil.date(),
DataSizeUtil.format(fileSize));
LinkedMultiValueMap<String, String> headers = new LinkedMultiValueMap<>();
reportConfig.getCustomHeaders().forEach(headers::add);
request.getHeaderNames().asIterator().forEachRemaining(headerKey -> {
String header = request.getHeader(headerKey);
headers.add(headerKey, header);
});
log.info("上报调用信息 {}", reportReq);
reportApi.report(headers, reportReq);
}
}

View File

@ -12,6 +12,7 @@ import cn.skcks.docking.gb28181.wvp.orm.mybatis.dynamic.model.WvpProxyDevice;
import cn.skcks.docking.gb28181.wvp.orm.mybatis.dynamic.model.WvpProxyDocking;
import cn.skcks.docking.gb28181.wvp.service.docking.DockingService;
import cn.skcks.docking.gb28181.wvp.service.ffmpeg.FfmpegSupportService;
import cn.skcks.docking.gb28181.wvp.service.report.ReportService;
import cn.skcks.docking.gb28181.wvp.sip.request.SipRequestBuilder;
import cn.skcks.docking.gb28181.wvp.sip.sender.SipSender;
import jakarta.servlet.AsyncContext;
@ -51,6 +52,7 @@ public class VideoService {
private final DockingService dockingService;
private final SipSender sender;
private final FfmpegConfig ffmpegConfig;
private final ReportService reportService;
private final ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
/**
@ -231,7 +233,7 @@ public class VideoService {
* @param time 录制时长 (单位: )
*/
@SneakyThrows
public void ffmpegRecord(ServletResponse response, String url, long time, WvpProxyDevice device,String callId){
public void ffmpegRecord(HttpServletRequest request, ServletResponse response, String url, long time, WvpProxyDevice device,String callId){
String tmpDir = ffmpegConfig.getTmpDir();
String fileName = callId + ".mp4";
File file = new File(tmpDir, fileName);
@ -274,7 +276,9 @@ public class VideoService {
log.info("临时文件 {}(大小 {})", file.getAbsolutePath(), file.length());
IoUtil.copy(new FileInputStream(file), servletOutputStream);
response.flushBuffer();
reportService.report(request, device, startTime, endTime, file.length());
} catch (Exception e){
reportService.report(request, device, startTime, endTime, -1);
log.error("写入 http 响应异常: {}", e.getMessage());
} finally {
System.gc();
@ -284,6 +288,8 @@ public class VideoService {
}
}
/**
* 录制视频 并写入 异步响应
* @param response AsyncContext.getResponse 异步响应

View File

@ -28,6 +28,7 @@ import cn.skcks.docking.gb28181.wvp.proxy.WvpProxyClient;
import cn.skcks.docking.gb28181.wvp.service.device.DeviceService;
import cn.skcks.docking.gb28181.wvp.service.docking.DockingService;
import cn.skcks.docking.gb28181.wvp.service.download.DownloadService;
import cn.skcks.docking.gb28181.wvp.service.report.ReportService;
import cn.skcks.docking.gb28181.wvp.service.video.VideoService;
import cn.skcks.docking.gb28181.wvp.utils.RetryUtil;
import com.github.rholder.retry.*;
@ -65,6 +66,8 @@ public class WvpService {
private final ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
private final ConcurrentMap<String, ScheduledFuture<?>> playing = new ConcurrentHashMap<>();
private final ReportService reportService;
public void header(HttpServletResponse response) {
response.setContentType("video/mp4");
response.setHeader("Accept-Ranges", "none");
@ -113,6 +116,7 @@ public class WvpService {
String reason = MessageFormat.format("调用 wvp api 查询设备({0})历史失败, 异常: {1}", deviceCode, e.getMessage());
writeErrorToResponse(asyncResponse, JsonResponse.error(reason));
} finally {
reportService.report(request,wvpProxyDevice, startTime, endTime,-1);
log.info("asyncContext 结束");
asyncContext.complete();
}

View File

@ -9,7 +9,8 @@ import org.springframework.scheduling.annotation.EnableAsync;
@EnableFeignClients(basePackages = {
"cn.skcks.docking.gb28181.media",
"cn.skcks.docking.gb28181.wvp.proxy"
"cn.skcks.docking.gb28181.wvp.proxy",
"cn.skcks.docking.gb28181.wvp.service.report"
})
@SpringBootApplication
@ComponentScan(basePackages = {

View File

@ -105,3 +105,9 @@ ffmpeg-support:
# [可选] 日志配置, 一般不需要改
logging:
config: classpath:logback.xml
report:
enabled: false
url: http://127.0.0.1:8080/api/report
custom-headers:
agent: gb28181-proxy

View File

@ -99,3 +99,10 @@ ffmpeg-support:
# [可选] 日志配置, 一般不需要改
logging:
config: classpath:logback.xml
report:
enabled: false
url: http://127.0.0.1:8080/api/report
custom-headers:
agent: gb28181-proxy