diff --git a/gb28181-wvp-proxy-api/src/main/java/cn/skcks/docking/gb28181/wvp/api/video/RecordController.java b/gb28181-wvp-proxy-api/src/main/java/cn/skcks/docking/gb28181/wvp/api/video/RecordController.java index b792a33..2af9c86 100644 --- a/gb28181-wvp-proxy-api/src/main/java/cn/skcks/docking/gb28181/wvp/api/video/RecordController.java +++ b/gb28181-wvp-proxy-api/src/main/java/cn/skcks/docking/gb28181/wvp/api/video/RecordController.java @@ -1,6 +1,8 @@ package cn.skcks.docking.gb28181.wvp.api.video; import cn.skcks.docking.gb28181.wvp.service.video.RecordService; +import jakarta.servlet.AsyncContext; +import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -19,7 +21,7 @@ public class RecordController { } @GetMapping - public void record(HttpServletResponse response, @RequestParam String url,@RequestParam long time){ - recordService.record(response,url,time); + public void record(HttpServletRequest request, HttpServletResponse response, @RequestParam String url, @RequestParam long time){ + recordService.record(request, response,url,time); } } diff --git a/gb28181-wvp-proxy-service/src/main/java/cn/skcks/docking/gb28181/wvp/service/video/RecordService.java b/gb28181-wvp-proxy-service/src/main/java/cn/skcks/docking/gb28181/wvp/service/video/RecordService.java index 4dbfc54..699117e 100644 --- a/gb28181-wvp-proxy-service/src/main/java/cn/skcks/docking/gb28181/wvp/service/video/RecordService.java +++ b/gb28181-wvp-proxy-service/src/main/java/cn/skcks/docking/gb28181/wvp/service/video/RecordService.java @@ -2,12 +2,17 @@ package cn.skcks.docking.gb28181.wvp.service.video; import cn.hutool.core.io.IoUtil; import cn.hutool.core.util.IdUtil; +import cn.skcks.docking.gb28181.wvp.executor.DefaultVideoExecutor; +import jakarta.servlet.AsyncContext; +import jakarta.servlet.ServletResponse; +import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.bytedeco.ffmpeg.global.avcodec; import org.bytedeco.ffmpeg.global.avutil; import org.bytedeco.javacv.*; +import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import java.io.*; @@ -20,62 +25,71 @@ import java.util.concurrent.atomic.AtomicBoolean; @Slf4j @Service public class RecordService { - public void header(HttpServletResponse response){ + public void header(HttpServletResponse response) { response.setContentType("video/mp4"); - response.setHeader("Accept-Ranges","none"); - response.setHeader("Connection","close"); + response.setHeader("Accept-Ranges", "none"); + response.setHeader("Connection", "close"); } @SneakyThrows - public void record(HttpServletResponse response, String url, long timeout){ - response.reset(); - // response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE); - header(response); + public void record(HttpServletRequest request, HttpServletResponse response, String url, long timeout) { + AsyncContext asyncContext = request.startAsync(); + asyncContext.start(() -> { + header(response); + record(asyncContext.getResponse(), url, timeout); + log.info("record 结束"); + asyncContext.complete(); + }); + } + @SneakyThrows + public void record(ServletResponse response, String url, long timeout) { + // response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE); Path tmp = Path.of(System.getProperty("java.io.tmpdir"), IdUtil.getSnowflakeNextIdStr()).toAbsolutePath(); File file = new File(tmp + ".mp4"); log.info("创建文件 {}, {}", file, file.createNewFile()); log.info("url {}", url); - try (FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(url)) { - grabber.start(); - try(FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(file, grabber.getImageWidth(), grabber.getImageHeight(),grabber.getAudioChannels())){ - recorder.start(); - log.info("开始录像"); - log.info("{}", file); - recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264); - recorder.setPixelFormat(avutil.AV_PIX_FMT_YUV420P); //视频源数据yuv - recorder.setAudioCodec(avcodec.AV_CODEC_ID_AAC); //设置音频压缩方式 - recorder.setFormat("mp4"); - recorder.setVideoOption("threads", String.valueOf(Runtime.getRuntime().availableProcessors())); //解码线程数 + FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(url); + grabber.start(); + FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(file, grabber.getImageWidth(), grabber.getImageHeight(), grabber.getAudioChannels()); + recorder.start(); + log.info("开始录像"); + log.info("{}", file); + recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264); + recorder.setPixelFormat(avutil.AV_PIX_FMT_YUV420P); // 视频源数据yuv + recorder.setAudioCodec(avcodec.AV_CODEC_ID_AAC); // 设置音频压缩方式 + recorder.setFormat("mp4"); + recorder.setVideoOption("threads", String.valueOf(Runtime.getRuntime().availableProcessors())); // 解码线程数 - ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor(); - AtomicBoolean record = new AtomicBoolean(true); - scheduledExecutorService.schedule(()->{ - log.info("到达超时时间, 结束录制"); - record.set(false); - }, timeout, TimeUnit.SECONDS); + ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor(); + AtomicBoolean record = new AtomicBoolean(true); + scheduledExecutorService.schedule(() -> { + log.info("到达超时时间, 结束录制"); + record.set(false); + }, timeout, TimeUnit.SECONDS); - try { - Frame frame; - while (record.get() && (frame = grabber.grab()) != null) { - recorder.record(frame); - } - grabber.stop(); - recorder.stop(); - } catch (FFmpegFrameRecorder.Exception | FrameGrabber.Exception e) { - throw new RuntimeException(e); - } + try { + Frame frame; + while (record.get() && (frame = grabber.grab()) != null) { + recorder.record(frame); } - } finally { - log.info("结束录制"); - InputStream inputStream = new BufferedInputStream(new FileInputStream(file)); - OutputStream outputStream = new BufferedOutputStream(response.getOutputStream()); - try{ - IoUtil.copy(inputStream, outputStream); - } catch (Exception ignore){} - log.info("临时文件 {} 写入 响应 完成", file); - log.info("删除临时文件 {} {}", file, file.delete()); + grabber.close(); + recorder.close(); + } catch (FFmpegFrameRecorder.Exception | FrameGrabber.Exception e) { + throw new RuntimeException(e); } + + log.info("结束录制"); + InputStream inputStream = new BufferedInputStream(new FileInputStream(file)); + OutputStream outputStream = new BufferedOutputStream(response.getOutputStream()); + try { + IoUtil.copy(inputStream, outputStream); + } catch (Exception ignore) { + } + log.info("临时文件 {} 写入 响应 完成", file); + log.info("删除临时文件 {} {}", file, file.delete()); + outputStream.close(); + } }