This commit is contained in:
parent
35edf66b6d
commit
21ed04bac5
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<StreamTask> startRecording(@RequestParam String streamUrl) {
|
||||
System.out.println("启动录制 :"+streamUrl);
|
||||
if(Objects.isNull(streamUrl)) {
|
||||
return Response.fail(-1);
|
||||
}
|
||||
|
||||
Response<StreamTask> 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<StreamTask> stopRecording(@RequestParam String streamUrl){
|
||||
|
||||
System.out.println("关闭录制 :"+streamUrl);
|
||||
if(Objects.isNull(streamUrl)) {
|
||||
return Response.fail(-1);
|
||||
}
|
||||
Response<StreamTask> 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<StreamTask> getLastTask(@RequestParam String streamUrl){
|
||||
|
||||
System.out.println("查看录制 :"+streamUrl);
|
||||
if(Objects.isNull(streamUrl)) {
|
||||
return Response.fail(-1);
|
||||
}
|
||||
Response<StreamTask> response = Response.success(taskService.getLastTask(streamUrl));
|
||||
System.out.println("查看录制返回 :"+ JSON.toJSONString(response));
|
||||
return response;
|
||||
}
|
||||
|
||||
|
||||
@PostMapping("search")
|
||||
public Response<PageInfo<Mp4Info>> 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<Mp4Info> 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<Mp4Info> pageInfo = new PageInfo<>(mp4s,request.getPageIndex(),request.getPageSize());
|
||||
System.out.println("查看录制 search 返回:"+JSON.toJSONString(request));
|
||||
return Response.success(pageInfo);
|
||||
}
|
||||
|
||||
@GetMapping("history")
|
||||
public Response<List<Mp4Info>> streamView(@RequestParam String streamUrl){
|
||||
System.out.println("查看录像历史返回 :"+ streamUrl);
|
||||
List<Mp4Info> dayMp4 = searchAll(streamUrl);
|
||||
dayMp4.sort((o1, o2) -> o2.getStartTime().compareTo(o1.getStartTime()));
|
||||
System.out.println("查看录像历史返回 :"+ JSON.toJSONString(dayMp4));
|
||||
return Response.success(dayMp4) ;
|
||||
}
|
||||
|
||||
public List<Mp4Info> searchAll(String streamUrl){
|
||||
|
||||
List<Mp4Info> dayMp4 = new ArrayList<>();
|
||||
List<DayRecord> dayRecords = iRecordService.findDaysPath();
|
||||
for(DayRecord dayRecord : dayRecords){
|
||||
List<StreamRecord> streamRecords = iRecordService.findInDayRecord(dayRecord);
|
||||
for(StreamRecord stream : streamRecords){
|
||||
if(stream.getStreamId().equals(streamUrl)){
|
||||
List<Mp4Record> 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
|
||||
|
|
|
|||
|
|
@ -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 <T> implements Serializable {
|
||||
|
||||
public PageInfo(List<T> 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<T> 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<T> getList() {
|
||||
return list;
|
||||
}
|
||||
|
||||
public PageInfo(int pageIndex) {
|
||||
this.pageIndex = pageIndex;
|
||||
}
|
||||
|
||||
public void setList(List<T> 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -28,4 +28,10 @@ public class Response<T> implements Serializable {
|
|||
r.code = 200;
|
||||
return r;
|
||||
}
|
||||
|
||||
public static <T> Response<T> fail(int code){
|
||||
Response<T> r = new Response<T>();
|
||||
r.code = code;
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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<DayRecord> 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<StreamRecord> streamRecords = dayRecord.getStreamRecords();
|
||||
for(StreamRecord streamRecord : streamRecords){
|
||||
List<FlvRecord> 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;
|
||||
// }
|
||||
|
||||
}
|
||||
|
|
@ -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<DayRecord> findDaysPath();
|
||||
|
||||
public List<StreamRecord> findInDayRecord(DayRecord dayRecord);
|
||||
|
||||
|
||||
|
||||
public void mergeMp4(FlvRecord flvRecord);
|
||||
|
||||
}
|
||||
|
|
@ -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<DayRecord> findDaysPath() {
|
||||
List<DayRecord> days = new ArrayList<>();
|
||||
List<String> 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<StreamRecord> findInDayRecord(DayRecord dayRecord) {
|
||||
return dayRecord.getStreamRecords();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mergeMp4(FlvRecord flvRecord) {
|
||||
try {
|
||||
processService.mergeMp4(flvRecord);
|
||||
}catch (Exception e){
|
||||
logger.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -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<StreamRecord> getStreamRecords() {
|
||||
List<StreamRecord> result = new ArrayList<>();
|
||||
List<String> 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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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<FlvRecord> queryFlvRecords(){
|
||||
|
||||
List<FlvRecord> result = new ArrayList<>();
|
||||
List<String> 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<Mp4Record> queryMp4Records(){
|
||||
|
||||
List<Mp4Record> result = new ArrayList<>();
|
||||
List<String> 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;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
package com.tuoheng.steam.service.dos;
|
||||
|
||||
public enum StreamType {
|
||||
Inner,Outer,LiveVideo
|
||||
}
|
||||
|
|
@ -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<Process> 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";
|
||||
|
|
|
|||
|
|
@ -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<Path> 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<String> readFiles(String stringPath) {
|
||||
|
||||
List<String> 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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
#ffmpeg=ffmpeg
|
||||
#recordPath=/Users/sunpeng/workspace/stream/record
|
||||
#livedates=7
|
||||
Loading…
Reference in New Issue