调整为异步 context

This commit is contained in:
shikong 2023-09-07 03:09:25 +08:00
parent 111d44b187
commit c1e9ce147f
2 changed files with 61 additions and 45 deletions

View File

@ -1,6 +1,8 @@
package cn.skcks.docking.gb28181.wvp.api.video; package cn.skcks.docking.gb28181.wvp.api.video;
import cn.skcks.docking.gb28181.wvp.service.video.RecordService; import cn.skcks.docking.gb28181.wvp.service.video.RecordService;
import jakarta.servlet.AsyncContext;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@ -19,7 +21,7 @@ public class RecordController {
} }
@GetMapping @GetMapping
public void record(HttpServletResponse response, @RequestParam String url,@RequestParam long time){ public void record(HttpServletRequest request, HttpServletResponse response, @RequestParam String url, @RequestParam long time){
recordService.record(response,url,time); recordService.record(request, response,url,time);
} }
} }

View File

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