From 951bffea4e19f8450efa1e0fd2e8931974671c38 Mon Sep 17 00:00:00 2001 From: shikong <919411476@qq.com> Date: Thu, 7 Sep 2023 11:32:27 +0800 Subject: [PATCH] =?UTF-8?q?=E6=94=B9=E4=B8=BA=20flv=20=E6=A0=BC=E5=BC=8F?= =?UTF-8?q?=20=E5=AE=9E=E6=97=B6=E6=B5=81=E4=B8=8B=E8=BD=BD=20=E4=B8=8D?= =?UTF-8?q?=E5=86=8D=E9=9C=80=E8=A6=81=E4=B8=B4=E6=97=B6=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E8=BD=AC=E5=82=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../wvp/service/video/RecordService.java | 83 +++++++++++-------- 1 file changed, 47 insertions(+), 36 deletions(-) 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 8af1b6f..a163655 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 @@ -1,23 +1,22 @@ package cn.skcks.docking.gb28181.wvp.service.video; -import cn.hutool.core.io.IoUtil; -import cn.hutool.core.util.IdUtil; 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.avcodec.AVPacket; import org.bytedeco.ffmpeg.global.avcodec; import org.bytedeco.ffmpeg.global.avutil; import org.bytedeco.javacv.FFmpegFrameGrabber; import org.bytedeco.javacv.FFmpegFrameRecorder; -import org.bytedeco.javacv.Frame; +import org.bytedeco.javacv.FFmpegLogCallback; import org.bytedeco.javacv.FrameGrabber; import org.springframework.stereotype.Service; -import java.io.*; -import java.nio.file.Path; +import java.io.ByteArrayOutputStream; +import java.io.OutputStream; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; @@ -27,7 +26,7 @@ import java.util.concurrent.atomic.AtomicBoolean; @Service public class RecordService { public void header(HttpServletResponse response) { - response.setContentType("video/mp4"); + response.setContentType("video/x-flv"); response.setHeader("Accept-Ranges", "none"); response.setHeader("Connection", "close"); } @@ -38,47 +37,68 @@ public class RecordService { asyncContext.setTimeout(0); asyncContext.start(() -> { header(response); - record(asyncContext.getResponse(), url, time, 60); - log.info("record 结束"); - asyncContext.complete(); + try{ + record(asyncContext.getResponse(), url, time); + } finally { + log.info("record 结束"); + asyncContext.complete(); + } }); } @SneakyThrows - public void record(ServletResponse response, String url, long time ,long timeout) { + public void record(ServletResponse response, String url, long time) { // 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()); +// Path tmp = Path.of(System.getProperty("java.io.tmpdir"), IdUtil.getSnowflakeNextIdStr()).toAbsolutePath(); +// File file = new File(tmp + ".mp4"); +// log.info("创建文件 {}, {}", file, file.createNewFile()); + ByteArrayOutputStream stream = new ByteArrayOutputStream(); + OutputStream outputStream = response.getOutputStream(); log.info("url {}", url); + FFmpegLogCallback.set(); 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); + FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(stream, grabber.getImageWidth(), grabber.getImageHeight(), grabber.getAudioChannels()); +// log.info("{}", file); + recorder.setInterleaved(true); + recorder.setVideoOption("preset", "ultrafast"); + recorder.setVideoOption("tune", "zerolatency"); + recorder.setVideoOption("crf", "25"); + recorder.setFrameRate(grabber.getFrameRate()); + recorder.setSampleRate(grabber.getSampleRate()); + recorder.setOption("flvflags","no_duration_filesize"); + if (grabber.getAudioChannels() > 0) { + recorder.setAudioChannels(grabber.getAudioChannels()); + recorder.setAudioBitrate(grabber.getAudioBitrate()); + recorder.setAudioCodec(grabber.getAudioCodec()); + } + recorder.setVideoBitrate(grabber.getVideoBitrate()); +// recorder.setVideoCodec(grabber.getVideoCodec()); 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.setFormat("flv"); recorder.setVideoOption("threads", String.valueOf(Runtime.getRuntime().availableProcessors())); // 解码线程数 + recorder.start(grabber.getFormatContext()); + log.info("开始录像"); ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor(); AtomicBoolean record = new AtomicBoolean(true); scheduledExecutorService.schedule(() -> { - log.info("到达超时时间, 结束录制"); + log.info("到达结束时间, 结束录制"); record.set(false); - }, time + timeout, TimeUnit.SECONDS); - long maxTime = TimeUnit.SECONDS.toMicros(time); + }, time, TimeUnit.SECONDS); try { - Frame frame; - while (!recorder.isCloseOutputStream() && record.get() && (frame = grabber.grab()) != null) { - long micros = TimeUnit.MICROSECONDS.toMicros(recorder.getTimestamp()); - if(micros >= maxTime){ - break; + AVPacket k; + while (record.get() && (k = grabber.grabPacket()) != null) { + recorder.recordPacket(k); + if(stream.size() > 0){ + outputStream.write(stream.toByteArray()); + outputStream.flush(); + stream.reset(); } - recorder.record(frame); + avcodec.av_packet_unref(k); } grabber.close(); recorder.close(); @@ -87,15 +107,6 @@ public class RecordService { } 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(); - } }