From 21ed04bac5f4055455781420b336b77c91423e6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AD=99=E5=B0=8F=E4=BA=91?= Date: Wed, 12 Mar 2025 15:32:52 +0800 Subject: [PATCH] xx --- .../com/tuoheng/steam/StreamApplication.java | 3 +- .../controller/StreamRecordController.java | 100 +++++++++++++++++- .../steam/controller/dto/PageInfo.java | 71 +++++++++++++ .../controller/dto/PageStreamRequest.java | 53 ++++++++++ .../steam/controller/dto/Response.java | 6 ++ .../java/com/tuoheng/steam/dos/Mp4Info.java | 28 +++++ .../com/tuoheng/steam/schedule/Scheduler.java | 70 ++++++++++++ .../tuoheng/steam/service/IRecordService.java | 18 ++++ .../tuoheng/steam/service/RecordService.java | 67 ++++++++++++ .../tuoheng/steam/service/dos/DayRecord.java | 50 +++++++++ .../tuoheng/steam/service/dos/FlvRecord.java | 44 ++++++++ .../tuoheng/steam/service/dos/Mp4Record.java | 45 ++++++++ .../steam/service/dos/StreamRecord.java | 95 +++++++++++++++++ .../tuoheng/steam/service/dos/StreamType.java | 5 + .../service/innerService/ProcessService.java | 61 +++++++++++ .../java/com/tuoheng/steam/util/FileUtil.java | 88 +++++++++++++++ .../com/tuoheng/steam/util/TimeUtils.java | 19 +++- src/main/resources/application.properties | 6 +- 18 files changed, 823 insertions(+), 6 deletions(-) create mode 100644 src/main/java/com/tuoheng/steam/controller/dto/PageInfo.java create mode 100644 src/main/java/com/tuoheng/steam/controller/dto/PageStreamRequest.java create mode 100644 src/main/java/com/tuoheng/steam/dos/Mp4Info.java create mode 100644 src/main/java/com/tuoheng/steam/schedule/Scheduler.java create mode 100644 src/main/java/com/tuoheng/steam/service/IRecordService.java create mode 100644 src/main/java/com/tuoheng/steam/service/RecordService.java create mode 100644 src/main/java/com/tuoheng/steam/service/dos/DayRecord.java create mode 100644 src/main/java/com/tuoheng/steam/service/dos/FlvRecord.java create mode 100644 src/main/java/com/tuoheng/steam/service/dos/Mp4Record.java create mode 100644 src/main/java/com/tuoheng/steam/service/dos/StreamRecord.java create mode 100644 src/main/java/com/tuoheng/steam/service/dos/StreamType.java create mode 100644 src/main/java/com/tuoheng/steam/util/FileUtil.java diff --git a/src/main/java/com/tuoheng/steam/StreamApplication.java b/src/main/java/com/tuoheng/steam/StreamApplication.java index 441364e..840dbab 100644 --- a/src/main/java/com/tuoheng/steam/StreamApplication.java +++ b/src/main/java/com/tuoheng/steam/StreamApplication.java @@ -2,11 +2,12 @@ package com.tuoheng.steam; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.scheduling.annotation.EnableScheduling; +@EnableScheduling @SpringBootApplication public class StreamApplication { - public static void main(String[] args) { SpringApplication.run(StreamApplication.class, args); } diff --git a/src/main/java/com/tuoheng/steam/controller/StreamRecordController.java b/src/main/java/com/tuoheng/steam/controller/StreamRecordController.java index addef24..2f12947 100644 --- a/src/main/java/com/tuoheng/steam/controller/StreamRecordController.java +++ b/src/main/java/com/tuoheng/steam/controller/StreamRecordController.java @@ -2,17 +2,25 @@ package com.tuoheng.steam.controller; import com.alibaba.fastjson2.JSON; -import com.google.api.client.json.Json; +import com.tuoheng.steam.controller.dto.PageInfo; +import com.tuoheng.steam.controller.dto.PageStreamRequest; import com.tuoheng.steam.controller.dto.Response; +import com.tuoheng.steam.dos.Mp4Info; import com.tuoheng.steam.dos.StreamTask; +import com.tuoheng.steam.service.IRecordService; import com.tuoheng.steam.service.ITaskService; -import com.tuoheng.steam.service.TaskService; +import com.tuoheng.steam.service.dos.DayRecord; +import com.tuoheng.steam.service.dos.Mp4Record; +import com.tuoheng.steam.service.dos.StreamRecord; +import org.apache.logging.log4j.util.Strings; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; -import java.util.Objects; +import java.io.File; +import java.util.*; +import java.util.stream.Collectors; @RestController() @RequestMapping("/record") @@ -23,9 +31,16 @@ public class StreamRecordController { @Autowired ITaskService taskService; + @Autowired + IRecordService iRecordService; + @GetMapping("start") public Response startRecording(@RequestParam String streamUrl) { System.out.println("启动录制 :"+streamUrl); + if(Objects.isNull(streamUrl)) { + return Response.fail(-1); + } + Response response = Response.success(taskService.startTask(streamUrl)); System.out.println("启动录制返回 :"+ JSON.toJSONString(response)); return response; @@ -33,7 +48,11 @@ public class StreamRecordController { @GetMapping("stop") public Response stopRecording(@RequestParam String streamUrl){ + System.out.println("关闭录制 :"+streamUrl); + if(Objects.isNull(streamUrl)) { + return Response.fail(-1); + } Response response = Response.success(taskService.stopTask(streamUrl)); if(Objects.isNull(response.getData().getOutFileName()) || response.getData().getOutFileName().isEmpty()){ response.setCode(500); @@ -44,12 +63,87 @@ public class StreamRecordController { @GetMapping("info") public Response getLastTask(@RequestParam String streamUrl){ + System.out.println("查看录制 :"+streamUrl); + if(Objects.isNull(streamUrl)) { + return Response.fail(-1); + } Response response = Response.success(taskService.getLastTask(streamUrl)); System.out.println("查看录制返回 :"+ JSON.toJSONString(response)); return response; } + + @PostMapping("search") + public Response> streamView(@RequestBody PageStreamRequest request){ + System.out.println("查看录制 search :"+JSON.toJSONString(request)); + + if(Objects.isNull(request.getPageIndex()) || Objects.isNull(request.getPageSize()) + || request.getPageIndex() <0 || request.getPageSize() <=0 ){ + System.out.println("查看录制 search 入参错误!"); + return Response.fail(-100); + } + + List mp4s = searchAll(request.getStreamId()); + + if(!Strings.isBlank(request.getStartTime())){ + long filerTime = Long.parseLong(request.getStartTime()) - 15 * 60 * 1000; + mp4s = mp4s.stream() + .filter(info -> info.getStartTime().compareTo(Long.toString(filerTime)) >= 0) + .collect(Collectors.toList()); // 将结果收集到列表中 + } + + if(!Strings.isBlank(request.getEndTime())){ + long filerTime = Long.parseLong(request.getEndTime()); + mp4s = mp4s.stream() + .filter(info -> info.getStartTime().compareTo(Long.toString(filerTime)) <= 0) + .collect(Collectors.toList()); // 将结果收集到列表中 + } + /** + * 倒序排列 + */ + mp4s.sort((o1, o2) -> o2.getStartTime().compareTo(o1.getStartTime())); + PageInfo pageInfo = new PageInfo<>(mp4s,request.getPageIndex(),request.getPageSize()); + System.out.println("查看录制 search 返回:"+JSON.toJSONString(request)); + return Response.success(pageInfo); + } + + @GetMapping("history") + public Response> streamView(@RequestParam String streamUrl){ + System.out.println("查看录像历史返回 :"+ streamUrl); + List dayMp4 = searchAll(streamUrl); + dayMp4.sort((o1, o2) -> o2.getStartTime().compareTo(o1.getStartTime())); + System.out.println("查看录像历史返回 :"+ JSON.toJSONString(dayMp4)); + return Response.success(dayMp4) ; + } + + public List searchAll(String streamUrl){ + + List dayMp4 = new ArrayList<>(); + List dayRecords = iRecordService.findDaysPath(); + for(DayRecord dayRecord : dayRecords){ + List streamRecords = iRecordService.findInDayRecord(dayRecord); + for(StreamRecord stream : streamRecords){ + if(stream.getStreamId().equals(streamUrl)){ + List mp4Records = stream.queryMp4Records(); + if(Objects.nonNull(mp4Records) && !mp4Records.isEmpty()){ + for(Mp4Record mp4Record : mp4Records){ + Mp4Info mp4Info = new Mp4Info(); + mp4Info.setStartTime(mp4Record.getStartTime()); + mp4Info.setUrl(dayRecord.getDay()+ + File.separator + stream.getStreamId() + File.separator + mp4Record.getMp4()); + dayMp4.add(mp4Info); + } + } + } + } + } + return dayMp4; + } + + + + /** * 方便测试时候使用 * @return diff --git a/src/main/java/com/tuoheng/steam/controller/dto/PageInfo.java b/src/main/java/com/tuoheng/steam/controller/dto/PageInfo.java new file mode 100644 index 0000000..d132672 --- /dev/null +++ b/src/main/java/com/tuoheng/steam/controller/dto/PageInfo.java @@ -0,0 +1,71 @@ +package com.tuoheng.steam.controller.dto; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class PageInfo implements Serializable { + + public PageInfo(List list,int page,int pageSize) { + this.total = list.size(); + this.pageSize = pageSize; + this.pageIndex = page; + + // 计算分页数据 + int fromIndex = page * pageSize; // 起始索引 + int toIndex = Math.min(fromIndex + pageSize, total); // 结束索引 + + // 检查页码是否超出范围 + if (fromIndex >= total) { + // 如果页码超出范围,返回空列表 + this.list = Collections.emptyList(); + } else { + // 截取当前页的数据 + this.list = list.subList(fromIndex, toIndex); + } + } + + private List list; + private int total; + + public int getPageIndex() { + return pageIndex; + } + + public void setPageIndex(int pageIndex) { + this.pageIndex = pageIndex; + } + + private int pageSize; + private int pageIndex; + + public List getList() { + return list; + } + + public PageInfo(int pageIndex) { + this.pageIndex = pageIndex; + } + + public void setList(List list) { + this.list = list; + } + + public int getTotal() { + return total; + } + + public void setTotal(int total) { + this.total = total; + } + + public int getPageSize() { + return pageSize; + } + + public void setPageSize(int pageSize) { + this.pageSize = pageSize; + } + +} diff --git a/src/main/java/com/tuoheng/steam/controller/dto/PageStreamRequest.java b/src/main/java/com/tuoheng/steam/controller/dto/PageStreamRequest.java new file mode 100644 index 0000000..21ef226 --- /dev/null +++ b/src/main/java/com/tuoheng/steam/controller/dto/PageStreamRequest.java @@ -0,0 +1,53 @@ +package com.tuoheng.steam.controller.dto; + +import java.io.Serializable; + +public class PageStreamRequest implements Serializable { + String streamId; + Integer pageIndex ; + Integer pageSize; + String startTime; + String endTime; + + public String getStreamId() { + return streamId; + } + + public void setStreamId(String streamId) { + this.streamId = streamId; + } + + public Integer getPageIndex() { + return pageIndex; + } + + public void setPageIndex(Integer pageIndex) { + this.pageIndex = pageIndex; + } + + public Integer getPageSize() { + return pageSize; + } + + public void setPageSize(Integer pageSize) { + this.pageSize = pageSize; + } + + public String getStartTime() { + return startTime; + } + + public void setStartTime(String startTime) { + this.startTime = startTime; + } + + public String getEndTime() { + return endTime; + } + + public void setEndTime(String endTime) { + this.endTime = endTime; + } + + +} diff --git a/src/main/java/com/tuoheng/steam/controller/dto/Response.java b/src/main/java/com/tuoheng/steam/controller/dto/Response.java index a7407a9..d682e94 100644 --- a/src/main/java/com/tuoheng/steam/controller/dto/Response.java +++ b/src/main/java/com/tuoheng/steam/controller/dto/Response.java @@ -28,4 +28,10 @@ public class Response implements Serializable { r.code = 200; return r; } + + public static Response fail(int code){ + Response r = new Response(); + r.code = code; + return r; + } } diff --git a/src/main/java/com/tuoheng/steam/dos/Mp4Info.java b/src/main/java/com/tuoheng/steam/dos/Mp4Info.java new file mode 100644 index 0000000..310455e --- /dev/null +++ b/src/main/java/com/tuoheng/steam/dos/Mp4Info.java @@ -0,0 +1,28 @@ +package com.tuoheng.steam.dos; + +import java.io.Serializable; +import java.util.Date; + +public class Mp4Info implements Serializable { + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + + + String url; + String startTime; + + public String getStartTime() { + return startTime; + } + + public void setStartTime(String startTime) { + this.startTime = startTime; + } +} diff --git a/src/main/java/com/tuoheng/steam/schedule/Scheduler.java b/src/main/java/com/tuoheng/steam/schedule/Scheduler.java new file mode 100644 index 0000000..d363f77 --- /dev/null +++ b/src/main/java/com/tuoheng/steam/schedule/Scheduler.java @@ -0,0 +1,70 @@ +package com.tuoheng.steam.schedule; + +import com.tuoheng.steam.service.IRecordService; +import com.tuoheng.steam.service.TaskService; +import com.tuoheng.steam.service.dos.DayRecord; +import com.tuoheng.steam.service.dos.FlvRecord; +import com.tuoheng.steam.service.dos.Mp4Record; +import com.tuoheng.steam.service.dos.StreamRecord; +import com.tuoheng.steam.util.TimeUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; +import java.util.*; + + +@Component +public class Scheduler { + + private static final Logger logger = LoggerFactory.getLogger(Scheduler.class); + + @Autowired + IRecordService iRecordService; + + @Value("${livedates}") + private Integer livedates; + + /** + * 初次执行延迟6秒执行 + * 每隔 60 分钟执行一次 60*60*1000 + */ + @Scheduled(fixedRate = 3600000, initialDelay = 6000) + public void mergeTask() { + System.out.println("开始FLV到MP4的转换 - " + System.currentTimeMillis() / 1000); + List dayRecords = iRecordService.findDaysPath(); + for (int index = 0; index < dayRecords.size(); index++) { + + DayRecord dayRecord = dayRecords.get(index); + if(TimeUtils.isBefore(dayRecord.getDay(),livedates)){ + dayRecord.clear(); + } else { + List streamRecords = dayRecord.getStreamRecords(); + for(StreamRecord streamRecord : streamRecords){ + List flvRecords = streamRecord.queryFlvRecords(); + for(FlvRecord flvRecord : flvRecords){ + iRecordService.mergeMp4(flvRecord); + } + } + } + } + } + + + + +// public static boolean isWithin15Minutes(String timestamp1, String timestamp2) { +// // 将字符串转换为 long 类型 +// long time1 = Long.parseLong(timestamp1); +// long time2 = Long.parseLong(timestamp2); +// // 计算时间差的绝对值 +// long diff = Math.abs(time2 - time1); +// // 15 分钟 = 15 * 60 * 1000 毫秒 +// long fifteenMinutesInMillis = 15 * 60 * 1000; +// // 判断是否小于或等于 15 分钟 +// return diff <= fifteenMinutesInMillis; +// } + +} diff --git a/src/main/java/com/tuoheng/steam/service/IRecordService.java b/src/main/java/com/tuoheng/steam/service/IRecordService.java new file mode 100644 index 0000000..ded0f48 --- /dev/null +++ b/src/main/java/com/tuoheng/steam/service/IRecordService.java @@ -0,0 +1,18 @@ +package com.tuoheng.steam.service; + +import com.tuoheng.steam.service.dos.*; + +import java.util.Date; +import java.util.List; + +public interface IRecordService { + + public List findDaysPath(); + + public List findInDayRecord(DayRecord dayRecord); + + + + public void mergeMp4(FlvRecord flvRecord); + +} diff --git a/src/main/java/com/tuoheng/steam/service/RecordService.java b/src/main/java/com/tuoheng/steam/service/RecordService.java new file mode 100644 index 0000000..de15658 --- /dev/null +++ b/src/main/java/com/tuoheng/steam/service/RecordService.java @@ -0,0 +1,67 @@ +package com.tuoheng.steam.service; + +import com.tuoheng.steam.service.dos.*; +import com.tuoheng.steam.service.innerService.ProcessService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import java.io.File; +import java.util.*; +import java.util.stream.Collectors; + +import static com.tuoheng.steam.util.FileUtil.readFiles; + +@Component +public class RecordService implements IRecordService { + + @Value("${recordPath}") + private String recordPath; + + @Autowired + ProcessService processService; + + private static final Logger logger = LoggerFactory.getLogger(IRecordService.class); + + public static boolean isValidDate(String dateStr) { + // 正则表达式:4位年份 + 2位月份 + 2位日期 + String regex = "^\\d{4}(0[1-9]|1[0-2])(0[1-9]|[12][0-9]|3[01])$"; + return dateStr.matches(regex); + } + + @Override + public List findDaysPath() { + List days = new ArrayList<>(); + List fileNames = readFiles(recordPath); + for (String fileName : fileNames) { + if(isValidDate(fileName)){ + DayRecord day = new DayRecord(); + day.setRoot(recordPath); + day.setDay(fileName); + days.add(day); + } + } + days.sort(Comparator.comparing(DayRecord::getDay)); + return days; + } + + + @Override + public List findInDayRecord(DayRecord dayRecord) { + return dayRecord.getStreamRecords(); + } + + @Override + public void mergeMp4(FlvRecord flvRecord) { + try { + processService.mergeMp4(flvRecord); + }catch (Exception e){ + logger.error(e.getMessage()); + } + } + + + +} diff --git a/src/main/java/com/tuoheng/steam/service/dos/DayRecord.java b/src/main/java/com/tuoheng/steam/service/dos/DayRecord.java new file mode 100644 index 0000000..0fc1a1c --- /dev/null +++ b/src/main/java/com/tuoheng/steam/service/dos/DayRecord.java @@ -0,0 +1,50 @@ +package com.tuoheng.steam.service.dos; + +import com.tuoheng.steam.util.FileUtil; + +import java.io.File; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; + +/** + * 代表日期的文件夹 + */ +public class DayRecord { + public String getRoot() { + return root; + } + + public void setRoot(String root) { + this.root = root; + } + + String root; + + String day; + + public String getDay() { + return day; + } + + public void setDay(String day) { + this.day = day; + } + + public void clear(){ + FileUtil.deleteFolder(root + File.separator + this.day); + } + + public List getStreamRecords() { + List result = new ArrayList<>(); + List fileList = FileUtil.readFiles(root + File.separator + day); + for(String fileName : fileList){ + StreamRecord record = new StreamRecord(); + record.setDayRecord(this); + record.setStreamId(fileName); + record.setStreamType(FileUtil.getStreamType(fileName)); + result.add(record); + } + return result; + } +} diff --git a/src/main/java/com/tuoheng/steam/service/dos/FlvRecord.java b/src/main/java/com/tuoheng/steam/service/dos/FlvRecord.java new file mode 100644 index 0000000..b588700 --- /dev/null +++ b/src/main/java/com/tuoheng/steam/service/dos/FlvRecord.java @@ -0,0 +1,44 @@ +package com.tuoheng.steam.service.dos; + +/** + * FLV文件 + */ +public class FlvRecord { + public String getFlv() { + return flv; + } + + public void setFlv(String flv) { + this.flv = flv; + } + + public StreamRecord getStream() { + return stream; + } + + public void setStream(StreamRecord stream) { + this.stream = stream; + } + + String flv; + StreamRecord stream; + + public String getStartTime() { + return extractNameWithoutExtension(flv); + } + + + public static String extractNameWithoutExtension(String fileName) { + // 找到最后一个 '.' 的位置 + int lastDotIndex = fileName.lastIndexOf('.'); + + // 如果文件名中没有 '.',则返回整个文件名 + if (lastDotIndex == -1) { + return fileName; + } + + // 截取从开头到最后一个 '.' 之前的部分 + return fileName.substring(0, lastDotIndex); + } + +} diff --git a/src/main/java/com/tuoheng/steam/service/dos/Mp4Record.java b/src/main/java/com/tuoheng/steam/service/dos/Mp4Record.java new file mode 100644 index 0000000..5a2fa1a --- /dev/null +++ b/src/main/java/com/tuoheng/steam/service/dos/Mp4Record.java @@ -0,0 +1,45 @@ +package com.tuoheng.steam.service.dos; + +/** + * MP4文件 + */ +public class Mp4Record { + + String mp4; + StreamRecord stream; + + + + public String getMp4() { + return mp4; + } + + public void setMp4(String mp4) { + this.mp4 = mp4; + } + + public StreamRecord getStream() { + return stream; + } + + public void setStream(StreamRecord stream) { + this.stream = stream; + } + + public String getStartTime() { + return extractNameWithoutExtension(mp4); + } + + public static String extractNameWithoutExtension(String fileName) { + // 找到最后一个 '.' 的位置 + int lastDotIndex = fileName.lastIndexOf('.'); + + // 如果文件名中没有 '.',则返回整个文件名 + if (lastDotIndex == -1) { + return fileName; + } + + // 截取从开头到最后一个 '.' 之前的部分 + return fileName.substring(0, lastDotIndex); + } +} diff --git a/src/main/java/com/tuoheng/steam/service/dos/StreamRecord.java b/src/main/java/com/tuoheng/steam/service/dos/StreamRecord.java new file mode 100644 index 0000000..0ced8b8 --- /dev/null +++ b/src/main/java/com/tuoheng/steam/service/dos/StreamRecord.java @@ -0,0 +1,95 @@ +package com.tuoheng.steam.service.dos; + +import com.tuoheng.steam.util.FileUtil; + +import java.io.File; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +/** + * 代表流的文件夹 + */ +public class StreamRecord { + public String getStreamId() { + return streamId; + } + + public void setStreamId(String streamId) { + this.streamId = streamId; + } + + public DayRecord getDayRecord() { + return dayRecord; + } + + public void setDayRecord(DayRecord dayRecord) { + this.dayRecord = dayRecord; + } + + public StreamType getStreamType() { + return streamType; + } + + public void setStreamType(StreamType streamType) { + this.streamType = streamType; + } + + String streamId; + StreamType streamType; + DayRecord dayRecord; + + public List queryFlvRecords(){ + + List result = new ArrayList<>(); + List fileList = FileUtil.readFiles( + dayRecord.root+ File.separator + dayRecord.day + File.separator + streamId); + + for(String fileName : fileList){ + if(FileUtil.isFlv(fileName)){ + FlvRecord flvRecord = new FlvRecord(); + flvRecord.setFlv(fileName); + + flvRecord.setStream(this); + result.add(flvRecord); + } + } + + result.sort((o1, o2) -> { + // 提取时间戳并转换为 long 类型 + long timestamp1 = Long.parseLong(o1.getFlv().split("\\.")[0]); + long timestamp2 = Long.parseLong(o2.getFlv().split("\\.")[0]); + // 按时间戳升序排序 + return Long.compare(timestamp1, timestamp2); + }); + + return result; + } + + public List queryMp4Records(){ + + List result = new ArrayList<>(); + List fileList = FileUtil.readFiles( + dayRecord.root+ File.separator + dayRecord.day + File.separator + streamId); + for(String fileName : fileList){ + if(FileUtil.isMp4(fileName)){ + Mp4Record record = new Mp4Record(); + record.setStream(this); + record.setMp4(fileName); + result.add(record); + } + } + + result.sort((o1, o2) -> { + // 提取时间戳并转换为 long 类型 + long timestamp1 = Long.parseLong(o1.mp4.split("\\.")[0]); + long timestamp2 = Long.parseLong(o2.mp4.split("\\.")[0]); + // 按时间戳升序排序 + return Long.compare(timestamp1, timestamp2); + }); + + return result; + } +} diff --git a/src/main/java/com/tuoheng/steam/service/dos/StreamType.java b/src/main/java/com/tuoheng/steam/service/dos/StreamType.java new file mode 100644 index 0000000..6d5b146 --- /dev/null +++ b/src/main/java/com/tuoheng/steam/service/dos/StreamType.java @@ -0,0 +1,5 @@ +package com.tuoheng.steam.service.dos; + +public enum StreamType { + Inner,Outer,LiveVideo +} diff --git a/src/main/java/com/tuoheng/steam/service/innerService/ProcessService.java b/src/main/java/com/tuoheng/steam/service/innerService/ProcessService.java index c5d9acb..a3a6346 100644 --- a/src/main/java/com/tuoheng/steam/service/innerService/ProcessService.java +++ b/src/main/java/com/tuoheng/steam/service/innerService/ProcessService.java @@ -2,6 +2,9 @@ package com.tuoheng.steam.service.innerService; import com.tuoheng.steam.dos.StreamProcess; import com.tuoheng.steam.dos.ProcessType; +import com.tuoheng.steam.service.dos.FlvRecord; +import com.tuoheng.steam.util.ProcessManager; +import com.tuoheng.steam.util.TimeUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; @@ -12,10 +15,14 @@ import java.io.File; import java.io.IOException; import java.io.InputStreamReader; import java.util.ArrayList; +import java.util.Date; import java.util.List; import java.util.UUID; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import java.util.stream.Collectors; @Service public class ProcessService { @@ -29,6 +36,9 @@ public class ProcessService { @Value("${srs.targetPath}") private String targetPath; + @Value("${recordPath}") + private String recordPath; + private static final Logger logger = LoggerFactory.getLogger(ProcessService.class); /** @@ -36,6 +46,57 @@ public class ProcessService { */ ExecutorService loggingService = Executors.newCachedThreadPool(); + public void mergeMp4(FlvRecord flvRecord) throws IOException, ExecutionException, InterruptedException { + + String command = ffmpeg + " -i "+ recordPath+ File.separator + + flvRecord.getStream().getDayRecord().getDay() + File.separator + + flvRecord.getStream().getStreamId() + File.separator + flvRecord.getFlv() + " -c:v copy -c:a aac " + recordPath+ File.separator + + flvRecord.getStream().getDayRecord().getDay() + File.separator + + flvRecord.getStream().getStreamId() + File.separator +flvRecord.getStartTime() + ".mp4"; + + + ProcessBuilder pb = new ProcessBuilder(command.split(" ")); + pb.redirectErrorStream(true); + Process process = pb.start(); + + loggingService.execute(new Runnable() { + @Override + public void run() { + try (BufferedReader reader = new BufferedReader( + new InputStreamReader(process.getInputStream()))) { + String line; + while ((line = reader.readLine()) != null) { + logger.info("mergeMp4-------- {}",line); + } + } catch (IOException e) { + logger.info("mergeMp4-------- Over",e); + } + logger.info("mergeMp4 Over"); + File delete = new File(recordPath+ File.separator + + flvRecord.getStream().getDayRecord().getDay() + File.separator + + flvRecord.getStream().getStreamId() + File.separator + flvRecord.getFlv()); + if(delete.exists()){ + delete.delete(); + } + } + }); + + CompletableFuture future = process.onExit(); + // 阻塞等待进程结束 + Process completedProcess = future.get(); + + // 检查进程是否成功结束 + if (completedProcess.exitValue() == 0) { + logger.info("进程成功结束!"); + /** + * 删除数据 + */ + } else { + logger.info("进程失败,退出码 {} " ,completedProcess.exitValue()); + } + + } + public StreamProcess recordStream(String streamUrl) throws IOException { String recordFileName = splitPath+'/'+UUID.randomUUID().toString() +".ts"; diff --git a/src/main/java/com/tuoheng/steam/util/FileUtil.java b/src/main/java/com/tuoheng/steam/util/FileUtil.java new file mode 100644 index 0000000..12ec691 --- /dev/null +++ b/src/main/java/com/tuoheng/steam/util/FileUtil.java @@ -0,0 +1,88 @@ +package com.tuoheng.steam.util; + +import com.tuoheng.steam.service.dos.DayRecord; +import com.tuoheng.steam.service.dos.StreamType; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Stream; + +public class FileUtil { + + public static void deleteFolder(String stringPath) { + + try { + Path path = Paths.get(stringPath); + if (Files.exists(path)) { + try (Stream pathStream = Files.walk(path)) { + pathStream.sorted((p1, p2) -> -p1.compareTo(p2)) // 从子文件/文件夹开始删除 + .forEach(p -> { + try { + Files.delete(p); // 删除文件或文件夹 + } catch (IOException e) { + System.err.println("无法删除: " + p + ", 原因: " + e.getMessage()); + } + }); + } + } else { + System.out.println("文件夹不存在: " + path); + } + }catch (Exception e){ + e.printStackTrace(); + } + + } + + public static List readFiles(String stringPath) { + + List list = new ArrayList<>(); + + File directory = new File(stringPath); + + // 检查目录是否存在且是一个目录 + if (directory.exists() && directory.isDirectory()) { + // 获取目录下的所有子文件和文件夹 + File[] files = directory.listFiles(); + + if (files != null) { + for (File file : files) { + list.add(file.getName()); + } + } else { + + } + } else { + + } + return list; + } + + public static boolean isTempFile(String fileName) { + String[] result = fileName.split("\\."); + return result.length == 3; + } + + public static boolean isFlv(String fileName) { + return fileName.endsWith("flv"); + } + + public static boolean isMp4(String fileName) { + return fileName.endsWith("mp4"); + } + + public static StreamType getStreamType(String fileName) { + if(fileName.endsWith("outer")){ + return StreamType.Outer; + }else if(fileName.endsWith("inner")){ + return StreamType.Inner; + }else{ + return StreamType.LiveVideo; + } + } + +} diff --git a/src/main/java/com/tuoheng/steam/util/TimeUtils.java b/src/main/java/com/tuoheng/steam/util/TimeUtils.java index 23f7d47..8456ec9 100644 --- a/src/main/java/com/tuoheng/steam/util/TimeUtils.java +++ b/src/main/java/com/tuoheng/steam/util/TimeUtils.java @@ -1,7 +1,8 @@ package com.tuoheng.steam.util; -import java.text.ParseException; import java.text.SimpleDateFormat; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; import java.util.Date; public class TimeUtils { @@ -12,4 +13,20 @@ public class TimeUtils { SimpleDateFormat sdf = new SimpleDateFormat(DATE_FORMAT); return sdf.format(date); } + + private static final DateTimeFormatter DAY_FORMAT = DateTimeFormatter.ofPattern("yyyyMMdd"); + + public static boolean isBefore(String dateStr,Integer days) { + + // 将字符串解析为 LocalDate 对象 + LocalDate date = LocalDate.parse(dateStr, DAY_FORMAT); + // 获取当前日期 + LocalDate currentDate = LocalDate.now(); + // 判断日期是否在 N 天之前 + if (date.isBefore(currentDate.minusDays(days))) { + return true; + } else { + return false; + } + } } diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 7a1575a..efde9dd 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -3,8 +3,12 @@ server.port = 8989 srs.splitPath=/data/java/srs/stream_server/temp srs.targetPath=/data/java/srs/srs/trunk/objs/nginx/html ffmpeg=/data/ffmpeg/bin/ffmpeg +recordPath=/data/java/srs/srs/trunk/objs/nginx/html/record +livedates=8 #server.port = 8080 #srs.splitPath=/Users/sunpeng/workspace/stream/temp #srs.targetPath=/Users/sunpeng/workspace/stream/html -#ffmpeg=ffmpeg \ No newline at end of file +#ffmpeg=ffmpeg +#recordPath=/Users/sunpeng/workspace/stream/record +#livedates=7 \ No newline at end of file