ffmpeg 转换视频流 试水, 未完

This commit is contained in:
shikong 2023-09-06 03:42:11 +08:00
parent af0229ac55
commit 6266f0c7d1
5 changed files with 180 additions and 11 deletions

View File

@ -1,6 +1,8 @@
package cn.skcks.docking.gb28181.wvp.api; package cn.skcks.docking.gb28181.wvp.api;
import cn.hutool.core.io.IoUtil; 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.config.SwaggerConfig;
import cn.skcks.docking.gb28181.wvp.proxy.ZlmProxyClient; import cn.skcks.docking.gb28181.wvp.proxy.ZlmProxyClient;
import feign.Response; import feign.Response;
@ -8,24 +10,34 @@ import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor; 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.springdoc.core.models.GroupedOpenApi;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseBody;
import java.io.IOException; import java.awt.image.BufferedImage;
import java.io.InputStream; import java.io.*;
import java.io.OutputStream; import java.nio.file.Path;
import static org.bytedeco.ffmpeg.global.avutil.AVMEDIA_TYPE_VIDEO;
@Slf4j
@Tag(name = "视频api") @Tag(name = "视频api")
@Controller @Controller
@RequestMapping("/video") @RequestMapping("/video")
@RequiredArgsConstructor @RequiredArgsConstructor
public class VideoController { public class VideoController {
private final ZlmProxyClient zlmProxyClient; private final ZlmProxyClient zlmProxyClient;
private final ZlmMediaConfig config;
@Bean @Bean
public GroupedOpenApi videoApi() { public GroupedOpenApi videoApi() {
@ -35,9 +47,9 @@ public class VideoController {
@Operation(summary = "获取视频") @Operation(summary = "获取视频")
@GetMapping(produces = MediaType.APPLICATION_OCTET_STREAM_VALUE) @GetMapping(produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
@ResponseBody @ResponseBody
public void video(HttpServletResponse response) { public ResponseEntity<File> video(HttpServletResponse response) throws IOException {
response.reset(); response.reset();
// response.setHeader("Content-Type","video/mp4"); response.setContentType("video/mp4");
// File f = new File("E:\\#Camera\\DCIM\\100MEDIA\\20230818\\NORM0003.MP4"); // File f = new File("E:\\#Camera\\DCIM\\100MEDIA\\20230818\\NORM0003.MP4");
// try ( // try (
// BufferedInputStream inputStream = new BufferedInputStream(new FileInputStream(f)); // BufferedInputStream inputStream = new BufferedInputStream(new FileInputStream(f));
@ -45,12 +57,89 @@ public class VideoController {
// IoUtil.copy(inputStream, outputStream); // 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")) { try (Response live = zlmProxyClient.live("live", "test.live.mp4")) {
InputStream inputStream = live.body().asInputStream(); // FFmpegLogCallback.set();
OutputStream outputStream = response.getOutputStream(); // live.body()
IoUtil.copy(inputStream, outputStream); // 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) { } catch (IOException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} finally {
file.deleteOnExit();
} }
// return ResponseEntity.noContent().build();
} }
} }

View File

@ -64,6 +64,27 @@
<artifactId>spring-test</artifactId> <artifactId>spring-test</artifactId>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<!-- <dependency>-->
<!-- <groupId>org.bytedeco</groupId>-->
<!-- <artifactId>javacv-platform</artifactId>-->
<!-- <version>1.5.6</version>-->
<!-- </dependency>-->
<!-- https://mvnrepository.com/artifact/org.bytedeco/javacv -->
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>javacv</artifactId>
<version>1.5.6</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.bytedeco/ffmpeg-platform -->
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>ffmpeg-platform</artifactId>
<version>4.4-1.5.6</version>
</dependency>
</dependencies> </dependencies>
<build> <build>

View File

@ -4,6 +4,7 @@ import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients; import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.ComponentScan;
import org.springframework.scheduling.annotation.EnableAsync;
@EnableFeignClients(basePackages = { @EnableFeignClients(basePackages = {
"cn.skcks.docking.gb28181.media", "cn.skcks.docking.gb28181.media",
@ -14,7 +15,9 @@ import org.springframework.context.annotation.ComponentScan;
"cn.skcks.docking.gb28181.annotation", "cn.skcks.docking.gb28181.annotation",
"cn.skcks.docking.gb28181.common", "cn.skcks.docking.gb28181.common",
"cn.skcks.docking.gb28181.wvp", "cn.skcks.docking.gb28181.wvp",
"cn.skcks.docking.gb28181.media",
}) })
@EnableAsync
public class Gb28181WvpProxyStarter { public class Gb28181WvpProxyStarter {
public static void main(String[] args) { public static void main(String[] args) {
SpringApplication.run(Gb28181WvpProxyStarter.class, args); SpringApplication.run(Gb28181WvpProxyStarter.class, args);

View File

@ -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

View File

@ -4,6 +4,7 @@ server:
project: project:
version: @project.version@ version: @project.version@
spring: spring:
data: data:
redis: redis:
@ -24,6 +25,14 @@ spring:
username: root username: root
password: 123456a password: 123456a
url: jdbc:mysql://192.168.1.241:3306/gb28181_docking_platform?createDatabaseIfNotExist=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai 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: media:
ip: 192.168.1.241 ip: 192.168.1.241