diff --git a/gb28181-wvp-proxy-api/src/main/java/cn/skcks/docking/gb28181/wvp/api/VideoController.java b/gb28181-wvp-proxy-api/src/main/java/cn/skcks/docking/gb28181/wvp/api/VideoController.java index b5012f6..a75372b 100644 --- a/gb28181-wvp-proxy-api/src/main/java/cn/skcks/docking/gb28181/wvp/api/VideoController.java +++ b/gb28181-wvp-proxy-api/src/main/java/cn/skcks/docking/gb28181/wvp/api/VideoController.java @@ -1,6 +1,8 @@ package cn.skcks.docking.gb28181.wvp.api; import cn.hutool.core.io.IoUtil; +import cn.hutool.core.util.IdUtil; +import cn.skcks.docking.gb28181.media.config.ZlmMediaConfig; import cn.skcks.docking.gb28181.wvp.config.SwaggerConfig; import cn.skcks.docking.gb28181.wvp.proxy.ZlmProxyClient; import feign.Response; @@ -8,24 +10,34 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.servlet.http.HttpServletResponse; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.bytedeco.ffmpeg.global.avcodec; +import org.bytedeco.ffmpeg.global.avutil; +import org.bytedeco.javacv.*; import org.springdoc.core.models.GroupedOpenApi; import org.springframework.context.annotation.Bean; import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; +import java.awt.image.BufferedImage; +import java.io.*; +import java.nio.file.Path; +import static org.bytedeco.ffmpeg.global.avutil.AVMEDIA_TYPE_VIDEO; + +@Slf4j @Tag(name = "视频api") @Controller @RequestMapping("/video") @RequiredArgsConstructor public class VideoController { private final ZlmProxyClient zlmProxyClient; + private final ZlmMediaConfig config; @Bean public GroupedOpenApi videoApi() { @@ -35,9 +47,9 @@ public class VideoController { @Operation(summary = "获取视频") @GetMapping(produces = MediaType.APPLICATION_OCTET_STREAM_VALUE) @ResponseBody - public void video(HttpServletResponse response) { + public ResponseEntity video(HttpServletResponse response) throws IOException { response.reset(); -// response.setHeader("Content-Type","video/mp4"); + response.setContentType("video/mp4"); // File f = new File("E:\\#Camera\\DCIM\\100MEDIA\\20230818\\NORM0003.MP4"); // try ( // BufferedInputStream inputStream = new BufferedInputStream(new FileInputStream(f)); @@ -45,12 +57,89 @@ public class VideoController { // IoUtil.copy(inputStream, outputStream); // } + // try{ + Path tmp = Path.of(System.getProperty("java.io.tmpdir"),IdUtil.getSnowflakeNextIdStr()).toAbsolutePath(); + File file = new File(tmp +".mp4"); + log.info("创建文件 {}, {}",file, file.createNewFile()); try (Response live = zlmProxyClient.live("live", "test.live.mp4")) { - InputStream inputStream = live.body().asInputStream(); - OutputStream outputStream = response.getOutputStream(); - IoUtil.copy(inputStream, outputStream); + // FFmpegLogCallback.set(); + // live.body() + // FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(live.body().asInputStream()); + + + String url = StringUtils.joinWith("/",config.getUrl(), "live","test.live.mp4"); + log.info("url {}",url); + FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(url); + grabber.start(); + + + try(FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(file,1920,1080,0)){ + 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())); //解码线程数 + try (Java2DFrameConverter converter = new Java2DFrameConverter()){ + + // 建议在线程中使用该方法 + Frame frame; + + // 此处仅为本地预览 + // CanvasFrame cframe = new CanvasFrame("欢迎来到直播间", CanvasFrame.getDefaultGamma() / + // grabber.getGamma()); + // cframe.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // 窗口关闭,则程序关闭 + // cframe.setAlwaysOnTop(true); + + while ((frame = grabber.grab()) != null) { + if(frame.streamIndex == AVMEDIA_TYPE_VIDEO){ + // BufferedImage bufferedImage = converter.getBufferedImage(frame); + // frame.image = converter.getFrame(bufferedImage).image; + recorder.record(frame); + } + // cframe.showImage(frame); + } + grabber.stop(); + grabber.release(); + recorder.stop(); + } catch (FFmpegFrameRecorder.Exception | FrameGrabber.Exception e) { + throw new RuntimeException(e); + } + } + log.info("录像结束"); + // response.getOutputStream().close(); + // } finally { + // log.info("临时文件 {} 写入完成",file); + // FileInputStream fileInputStream = new FileInputStream(file); + // OutputStream outputStream = response.getOutputStream(); + // IoUtil.copy(fileInputStream, outputStream); + // log.info("临时文件 {} 写入 响应 完成",file); + // fileInputStream.close(); + + return ResponseEntity.ok().body(file); + + // InputStream inputStream = live.body().asInputStream(); + // Path tmp = Path.of(System.getProperty("java.io.tmpdir"),IdUtil.getSnowflakeNextIdStr()).toAbsolutePath(); + // File file = File.createTempFile(tmp.toString(),".mp4"); + // log.info("临时文件 {}", file); + // FileOutputStream fileOutputStream = new FileOutputStream(file); + // BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileOutputStream); + // IoUtil.copy(inputStream, bufferedOutputStream); + // log.info("临时文件 {} 写入完成",file); + // FileInputStream fileInputStream = new FileInputStream(file); + // BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream); + // OutputStream outputStream = response.getOutputStream(); + // IoUtil.copy(bufferedInputStream, outputStream); + // log.info("临时文件 {} 写入 响应 完成",file); + // fileInputStream.close(); + // file.delete(); } catch (IOException e) { throw new RuntimeException(e); + } finally { + file.deleteOnExit(); } + // return ResponseEntity.noContent().build(); } } diff --git a/gb28181-wvp-proxy-service/pom.xml b/gb28181-wvp-proxy-service/pom.xml index 0f6eb9b..a6adc6f 100644 --- a/gb28181-wvp-proxy-service/pom.xml +++ b/gb28181-wvp-proxy-service/pom.xml @@ -64,6 +64,27 @@ spring-test test + + + + + + + + + + org.bytedeco + javacv + 1.5.6 + + + + + org.bytedeco + ffmpeg-platform + 4.4-1.5.6 + + @@ -87,4 +108,4 @@ - \ No newline at end of file + diff --git a/gb28181-wvp-proxy-starter/src/main/java/cn/skcks/docking/gb28181/wvp/Gb28181WvpProxyStarter.java b/gb28181-wvp-proxy-starter/src/main/java/cn/skcks/docking/gb28181/wvp/Gb28181WvpProxyStarter.java index b7d4cae..3cace52 100644 --- a/gb28181-wvp-proxy-starter/src/main/java/cn/skcks/docking/gb28181/wvp/Gb28181WvpProxyStarter.java +++ b/gb28181-wvp-proxy-starter/src/main/java/cn/skcks/docking/gb28181/wvp/Gb28181WvpProxyStarter.java @@ -4,6 +4,7 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.openfeign.EnableFeignClients; import org.springframework.context.annotation.ComponentScan; +import org.springframework.scheduling.annotation.EnableAsync; @EnableFeignClients(basePackages = { "cn.skcks.docking.gb28181.media", @@ -14,9 +15,11 @@ import org.springframework.context.annotation.ComponentScan; "cn.skcks.docking.gb28181.annotation", "cn.skcks.docking.gb28181.common", "cn.skcks.docking.gb28181.wvp", + "cn.skcks.docking.gb28181.media", }) +@EnableAsync public class Gb28181WvpProxyStarter { public static void main(String[] args) { SpringApplication.run(Gb28181WvpProxyStarter.class, args); } -} \ No newline at end of file +} diff --git a/gb28181-wvp-proxy-starter/src/main/resources/application-local.yml b/gb28181-wvp-proxy-starter/src/main/resources/application-local.yml new file mode 100644 index 0000000..b8215c9 --- /dev/null +++ b/gb28181-wvp-proxy-starter/src/main/resources/application-local.yml @@ -0,0 +1,47 @@ +server: + port: 18186 +project: + version: @project.version@ + +spring: + data: + redis: + # [必须修改] Redis服务器IP, REDIS安装在本机的,使用127.0.0.1 + # host: 192.168.1.241 + host: 10.10.10.200 + # [必须修改] 端口号 + port: 16379 + # [可选] 数据库 DB + database: 15 + # [可选] 访问密码,若你的redis服务器没有设置密码,就不需要用密码去连接 + password: 12341234 + # [可选] 超时时间 + timeout: 10000 + datasource: + type: com.zaxxer.hikari.HikariDataSource + driver-class-name: com.mysql.cj.jdbc.Driver + username: root + password: 12341234 + url: jdbc:mysql://10.10.10.200:3306/gb28181_docking_platform?createDatabaseIfNotExist=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai + config: + activate: + on-profile: local + cloud: + openfeign: + httpclient: + connection-timeout: 0 + ok-http: + read-timeout: 0 +media: + ip: 10.10.10.200 + url: 'http://10.10.10.200:5080' + # url: 'http://10.10.10.200:12580/anything/' + id: amrWMKmbKqoBjRQ9 +# secret: 035c73f7-bb6b-4889-a715-d9eb2d1925cc + secret: 4155cca6-2f9f-11ee-85e6-8de4ce2e7333 + +proxy: + wvp: + url: http://192.168.3.13:18978 + user: admin + passwd: admin diff --git a/gb28181-wvp-proxy-starter/src/main/resources/application.yml b/gb28181-wvp-proxy-starter/src/main/resources/application.yml index b13d785..714280f 100644 --- a/gb28181-wvp-proxy-starter/src/main/resources/application.yml +++ b/gb28181-wvp-proxy-starter/src/main/resources/application.yml @@ -4,6 +4,7 @@ server: project: version: @project.version@ + spring: data: redis: @@ -24,6 +25,14 @@ spring: username: root password: 123456a url: jdbc:mysql://192.168.1.241:3306/gb28181_docking_platform?createDatabaseIfNotExist=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai + profiles: + active: local + cloud: + openfeign: + httpclient: + connection-timeout: 0 + ok-http: + read-timeout: 0 media: ip: 192.168.1.241 @@ -37,4 +46,4 @@ proxy: wvp: url: http://192.168.3.13:18978 user: admin - passwd: admin \ No newline at end of file + passwd: admin