Compare commits

...

10 Commits

Author SHA1 Message Date
tangzh
be740def8d 视频拉流 2025-06-11 15:44:53 +08:00
tangzh
60cbbb5c1b 视频拉流 2025-06-11 15:39:53 +08:00
javpower
bbf75b981e
Merge pull request #2 from MagicToDo/main
fix(stream reader FFmpegFrameGrabber timeout parameter): stream reader "FFmpegFrameGrabber" timeout parameter setting fix
2024-10-08 09:48:53 +08:00
MagicLiu
a3398bc1ca fix(读流器FFmpegFrameGrabber超时参数): 读流器FFmpegFrameGrabber超时参数设置修复
经过反复测试,发现读流器FFmpegFrameGrabber超时参数设置有问题。导致grabber.grabPacket();阻塞
原:设置采集器构造超时时间(单位微秒,1秒=1000000微秒)
	grabber.setOption("stimeout", "5000000");
实际应为:
// 设置采集器构造超时时间(单位微秒,1秒=1000000微秒)
	grabber.setOption("timeout", "5000000");
2024-09-19 12:40:53 +08:00
xgc
7669895db7 添加预处理扩展接口 2024-07-10 11:01:28 +08:00
xgc
f960667ff4 更新README 2024-04-28 23:34:54 +08:00
xgc
1aba8c1014 更新README.en 2024-04-28 22:32:05 +08:00
javpower
f5487ca647
Create LICENSE 2024-04-28 20:35:40 +08:00
xgc
bb297dda31 更新README 2024-04-28 20:33:57 +08:00
xgc
37443ef64b 更新版本 2024-01-19 12:37:55 +08:00
32 changed files with 606 additions and 426 deletions

33
.gitignore vendored
View File

@ -1,33 +0,0 @@
README.md
target/
!.mvn/wrapper/maven-wrapper.jar
!**/src/main/**/target/
!**/src/test/**/target/
### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
build/
!**/src/main/**/build/
!**/src/test/**/build/
### VS Code ###
.vscode/

91
README.cn.md Normal file
View File

@ -0,0 +1,91 @@
<!-- Easy-FLV: Java RTSP/RTMP to FLV Converter -->
# 📺 Easy-FLV: Java 实现的 RTSP/RTMP 到 FLV 转换器
[![GitHub stars](https://img.shields.io/github/stars/javpower/easy-flv.svg)](https://github.com/javpower/easy-flv)
[![GitHub issues](https://img.shields.io/github/issues/javpower/easy-flv.svg)](https://github.com/javpower/easy-flv/issues)
[![Apache License 2.0](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
[![Java Version](https://img.shields.io/badge/java-1.8+-orange.svg)](https://adoptopenjdk.net/)
[![Spring Boot](https://img.shields.io/badge/spring--boot-2.7.+-blue.svg)](https://spring.io/projects/spring-boot)
## 🌟 关于 Easy-FLV
Easy-FLV 是一个用 Java 实现的库,它能够将 RTSP 或 RTMP 视频流转换为 FLV 格式,以便在浏览器中播放。它为实时视频监控、直播和视频流处理提供了一个高效、稳定且易于集成的解决方案。
### 为什么选择 Easy-FLV?
- **高效转换**:快速将视频流转换为 FLV 格式,无需复杂配置。
- **易于集成**:作为 Spring Boot Starter 使用,轻松集成到任何 Java 项目。
- **现代浏览器支持**:支持所有主流浏览器,无需额外插件。
- **实时流处理**:适用于实时视频流的转换,如安防监控和直播。
## 📄 效果图
以下是 Easy-FLV 在行动的效果图:
![img_1.png](img_1.png)
![img.png](img.png)
## 🚀 快速开始
### 添加 Maven 依赖
在你的 Spring Boot 项目中,添加以下 Maven 依赖:
```xml
<dependency>
<groupId>io.github.javpower</groupId>
<artifactId>rtsp-converter-flv-spring-boot-starter</artifactId>
<version>1.5.9.1</version>
</dependency>
```
### 实现接口
创建一个服务类来实现 `IOpenFLVService` 接口,并提供流地址:
```java
@Service
public class RtspDataService implements IOpenFLVService {
@Override
public String getUrl(Integer channel) {
// 根据 channel 获取 RTSP 视频流地址
return "rtsp://10.11.9.251:554/openUrl/16HV8mA";
}
}
```
### 配置 YAML
`application.yml` 中配置 Easy-FLV
```yaml
easy:
flv:
host: http://localhost:8200
```
### 使用接口
通过以下接口获取转换后的流地址,并在浏览器中播放:
- 转换地址:`GET http://ip:port/get/flv/hls/stream_{channel}.flv`
- 播放地址:`GET http://ip:port/flv/hls/stream_{channel}.flv`
### 直接使用
如果不使用接口,可以直接编码流地址并转换:
```java
public static void main(String[] args) throws UnsupportedEncodingException {
String url = "rtsp://XXXXXXXX";
String encodedUrl = java.net.URLEncoder.encode(url, "UTF-8");
System.out.println("Encoded Stream URL: " + encodedUrl);
}
```
- 转换地址:`GET http://ip:port/get/flv/hls/stream?url=编码后的地址`
- 播放地址:`GET http://ip:port/flv/hls/stream?url=编码后的地址`
## 🛠️ 贡献
我们欢迎任何形式的贡献,包括但不限于报告 bug、提交修复、添加新功能、改进文档等。
## 📄 许可证
Easy-FLV 根据 [Apache License 2.0](LICENSE) 发布。
## 📧 联系
- Email: [javpower@163.com](mailto:your-email@example.com)
- GitHub: [https://github.com/javpower/easy-flv](https://github.com/javpower/easy-flv)
- Gitee: [https://gitee.com/giteeClass/easy-flv](https://gitee.com/giteeClass/easy-flv)

View File

@ -1,59 +0,0 @@
# Easy-FLV
#### 项目介绍
rtsp、rtmp流地址转换成flv浏览器播放
极速开始
-------------------------------------
以下例子基于Spring Boot
### 第一步添加Maven依赖
直接添加以下maven依赖即可
```xml
<dependency>
<groupId>io.github.javpower</groupId>
<artifactId>rtsp-converter-flv-spring-boot-starter</artifactId>
<version>1.5.9</version>
</dependency>
```
### 第二步实现interface
数据库里面存储要播放的rtsp、rtmp流地址和自定义的通道号 实现类中通过通道号查询出地址<br>
```java
@Service
public class RtspDataService implements IOpenFLVService {
@Override
public String getUrl(Integer channel) {
//todo: 根据自定义的channel获取rtsp视频流地址
return "rtsp://10.11.9.251:554/openUrl/16HV8mA";
}
}
```
### 第三步配置yml
```yml
easy.flv.host=http://localhost:8200
```
### 第四步实现interface使用
- 流转换地址GET http://ip:port/get/flv/hls/stream_{channel}.flv
- 浏览器直接播放测试: GET http://ip:port/flv/hls/stream_{channel}.flv
### 第五步不想实现interface使用
原地址rtsp://XXXXXXXX <br>
```Java
public static void main(String[] args) throws UnsupportedEncodingException {
String url = "rtsp://XXXXXXXX";
String encodedUrl = java.net.URLEncoder.encode(url, "UTF-8");
System.out.println("编码:" + encodedUrl);
}
```
- 流转换地址GET http://ip:port/get/flv/hls/stream?url=编码后的地址
- 浏览器直接播放测试: GET http://ip:port/flv/hls/stream?url=编码后的地址
![img_1.png](img_1.png)
![img.png](img.png)

BIN
img.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 998 KiB

BIN
img_1.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 195 KiB

17
pom.xml
View File

@ -2,12 +2,12 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>io.github.javpower</groupId> <groupId>me.zhengjie</groupId>
<artifactId>rtsp-converter-flv-spring-boot-starter</artifactId> <artifactId>eladmin-flv</artifactId>
<version>1.5.9</version> <version>1.1</version>
<name>rtsp-converter-flv-spring-boot-starter</name> <name>视频模块</name>
<description>a tool about easy rtsp-converter-flv</description> <description>视频流转换 rtsp/rtmp 转flv</description>
<url>https://github.com/javpower/easy-flv</url>
<licenses> <licenses>
<license> <license>
<name> The Apache Software License, Version 2.0 </name> <name> The Apache Software License, Version 2.0 </name>
@ -38,6 +38,11 @@
</properties> </properties>
<dependencies> <dependencies>
<dependency>
<groupId>me.zhengjie</groupId>
<artifactId>eladmin-logging</artifactId>
<version>1.1</version>
</dependency>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId> <artifactId>spring-boot-starter</artifactId>

View File

@ -2,10 +2,16 @@ package com.gc.easy.flv.controller;
import com.gc.easy.flv.service.IFLVService; import com.gc.easy.flv.service.IFLVService;
import com.gc.easy.flv.service.IOpenFLVService; import com.gc.easy.flv.service.IOpenFLVService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import me.zhengjie.annotation.Log;
import me.zhengjie.annotation.rest.AnonymousGetMapping;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
@ -17,28 +23,25 @@ import java.io.UnsupportedEncodingException;
* *
* @author gc.x * @author gc.x
*/ */
@RestController @Slf4j
@RestController("api/front/flv")
@Api(tags = "微信:拉流播放")
public class FLVController { public class FLVController {
@Autowired @Autowired
private IFLVService service; private IFLVService service;
@Autowired(required = false) @Autowired(required = false)
private IOpenFLVService openFLVService; private IOpenFLVService openFLVService;
@GetMapping(value = "/get/flv/hls/stream_{channel}.flv") @Log("拉流播放")
public void open(@PathVariable(value = "channel") Integer channel, HttpServletResponse response, @ApiOperation(value = "打开视频流")
HttpServletRequest request) { // @GetMapping(value = "/get/stream")
String url = openFLVService.getUrl(channel); @AnonymousGetMapping(value = "/get/stream")
if(!StringUtils.isEmpty(url)){ public void open(String url, HttpServletResponse response, HttpServletRequest request) throws UnsupportedEncodingException {
service.open(channel,url, response, request);
}
}
@GetMapping(value = "/get/flv/hls/stream")
public void open1(String url, HttpServletResponse response,
HttpServletRequest request) throws UnsupportedEncodingException {
if (!StringUtils.isEmpty(url)) { if (!StringUtils.isEmpty(url)) {
String decodedUrl = java.net.URLDecoder.decode(url, "UTF-8"); String decodedUrl = java.net.URLDecoder.decode(url, "UTF-8");
service.open(decodedUrl, response, request); service.open(decodedUrl, response, request, openFLVService);
} }
} }
} }

View File

@ -1,39 +1,30 @@
package com.gc.easy.flv.controller; //package com.gc.easy.flv.controller;
//
import com.gc.easy.flv.config.FlvConfig; //import com.gc.easy.flv.config.FlvConfig;
import org.springframework.beans.factory.annotation.Autowired; //import me.zhengjie.annotation.rest.AnonymousGetMapping;
import org.springframework.stereotype.Controller; //import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.ui.Model; //import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping; //import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable; //
//import java.io.UnsupportedEncodingException;
import java.io.UnsupportedEncodingException; //
///**
/** // * FLV流转换
* FLV流转换 // *
* // * @author gc.x
* @author gc.x // */
*/ //@Controller
@Controller //public class FLVPlayController {
public class FLVPlayController { // @Autowired
@Autowired // private FlvConfig flvConfig;
private FlvConfig flvConfig; //
// @AnonymousGetMapping(value = "/flv/hls/stream")
@GetMapping(value = "/flv/hls/stream_{channel}.flv") // public String getAppHtml1(String url, Model model) throws UnsupportedEncodingException {
public String getAppHtml(@PathVariable(value = "channel") Integer channel, Model model) { // String decodedUrl = java.net.URLDecoder.decode(url, "UTF-8");
String videoPath=flvConfig.getHost()+"/get/flv/hls/stream_"+channel+".flv"; // String videoPath="/api/front/flv/get/stream?url="+decodedUrl;
model.addAttribute("videoPath", videoPath); // model.addAttribute("videoPath", videoPath);
model.addAttribute("wight", flvConfig.getWight()); // model.addAttribute("wight", flvConfig.getWight());
model.addAttribute("height", flvConfig.getHeight()); // model.addAttribute("height", flvConfig.getHeight());
return "video"; // return "video";
} // }
@GetMapping(value = "/flv/hls/stream") //}
public String getAppHtml1(String url, Model model) throws UnsupportedEncodingException {
String decodedUrl = java.net.URLDecoder.decode(url, "UTF-8");
String videoPath=flvConfig.getHost()+"/get/flv/hls/stream?url="+decodedUrl;
model.addAttribute("videoPath", videoPath);
model.addAttribute("wight", flvConfig.getWight());
model.addAttribute("height", flvConfig.getHeight());
return "video";
}
}

View File

@ -1,6 +1,7 @@
package com.gc.easy.flv.factories; package com.gc.easy.flv.factories;
import com.alibaba.fastjson.util.IOUtils; import com.alibaba.fastjson.util.IOUtils;
import com.gc.easy.flv.service.IOpenFLVService;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.bytedeco.ffmpeg.avcodec.AVPacket; import org.bytedeco.ffmpeg.avcodec.AVPacket;
import org.bytedeco.ffmpeg.global.avcodec; import org.bytedeco.ffmpeg.global.avcodec;
@ -60,12 +61,15 @@ public class ConverterFactories extends Thread implements Converter {
*/ */
private Map<String, Converter> factories; private Map<String, Converter> factories;
private IOpenFLVService openFLVService;
public ConverterFactories(String url, String key, Map<String, Converter> factories, List<AsyncContext> outEntitys) {
public ConverterFactories(String url, String key, Map<String, Converter> factories, List<AsyncContext> outEntitys, IOpenFLVService openFLVService) {
this.url = url; this.url = url;
this.key = key; this.key = key;
this.factories = factories; this.factories = factories;
this.outEntitys = outEntitys; this.outEntitys = outEntitys;
this.openFLVService = openFLVService;
} }
@Override @Override
@ -75,11 +79,11 @@ public class ConverterFactories extends Thread implements Converter {
grabber = new FFmpegFrameGrabber(url); grabber = new FFmpegFrameGrabber(url);
if ("rtsp".equals(url.substring(0, 4))) { if ("rtsp".equals(url.substring(0, 4))) {
grabber.setOption("rtsp_transport", "tcp"); grabber.setOption("rtsp_transport", "tcp");
grabber.setOption("stimeout", "5000000"); grabber.setOption("timeout", "5000000");
} }
grabber.start(); grabber.start();
if (avcodec.AV_CODEC_ID_H264 == grabber.getVideoCodec() if (avcodec.AV_CODEC_ID_H264 == grabber.getVideoCodec()
&& (grabber.getAudioChannels() == 0 || avcodec.AV_CODEC_ID_AAC == grabber.getAudioCodec())) { && (grabber.getAudioChannels() == 0 || avcodec.AV_CODEC_ID_AAC == grabber.getAudioCodec()) && (openFLVService == null || !openFLVService.openPreview())) {
log.info("this url:{} converterFactories start", url); log.info("this url:{} converterFactories start", url);
// 来源视频H264格式,音频AAC格式 // 来源视频H264格式,音频AAC格式
// 无须转码更低的资源消耗更低的延迟 // 无须转码更低的资源消耗更低的延迟
@ -135,7 +139,7 @@ public class ConverterFactories extends Thread implements Converter {
} else { } else {
isCloseGrabberAndResponse = false; isCloseGrabberAndResponse = false;
// 需要转码为视频H264格式,音频AAC格式 // 需要转码为视频H264格式,音频AAC格式
ConverterTranFactories c = new ConverterTranFactories(url, key, factories, outEntitys, grabber); ConverterTranFactories c = new ConverterTranFactories(url, key, factories, outEntitys, grabber, openFLVService);
factories.put(key, c); factories.put(key, c);
c.start(); c.start();
} }

View File

@ -1,11 +1,14 @@
package com.gc.easy.flv.factories; package com.gc.easy.flv.factories;
import com.alibaba.fastjson.util.IOUtils; import com.alibaba.fastjson.util.IOUtils;
import com.gc.easy.flv.service.IOpenFLVService;
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.javacv.FFmpegFrameGrabber; import org.bytedeco.javacv.FFmpegFrameGrabber;
import org.bytedeco.javacv.FFmpegFrameRecorder; import org.bytedeco.javacv.FFmpegFrameRecorder;
import org.bytedeco.javacv.Frame; import org.bytedeco.javacv.Frame;
import org.bytedeco.javacv.OpenCVFrameConverter;
import org.bytedeco.opencv.opencv_core.IplImage;
import javax.servlet.AsyncContext; import javax.servlet.AsyncContext;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
@ -59,13 +62,18 @@ public class ConverterTranFactories extends Thread implements Converter {
*/ */
private Map<String, Converter> factories; private Map<String, Converter> factories;
private OpenCVFrameConverter.ToIplImage converter;
private IOpenFLVService openFLVService;
public ConverterTranFactories(String url, String key, Map<String, Converter> factories, public ConverterTranFactories(String url, String key, Map<String, Converter> factories,
List<AsyncContext> outEntitys, FFmpegFrameGrabber grabber) { List<AsyncContext> outEntitys, FFmpegFrameGrabber grabber, IOpenFLVService openFLVService) {
this.url = url; this.url = url;
this.key = key; this.key = key;
this.factories = factories; this.factories = factories;
this.outEntitys = outEntitys; this.outEntitys = outEntitys;
this.grabber = grabber; this.grabber = grabber;
this.openFLVService=openFLVService;
} }
@Override @Override
@ -82,6 +90,8 @@ public class ConverterTranFactories extends Thread implements Converter {
stream = new ByteArrayOutputStream(); stream = new ByteArrayOutputStream();
recorder = new FFmpegFrameRecorder(stream, grabber.getImageWidth(), grabber.getImageHeight(), recorder = new FFmpegFrameRecorder(stream, grabber.getImageWidth(), grabber.getImageHeight(),
grabber.getAudioChannels()); grabber.getAudioChannels());
converter = new OpenCVFrameConverter.ToIplImage();
recorder.setInterleaved(true); recorder.setInterleaved(true);
recorder.setVideoOption("preset", "ultrafast"); recorder.setVideoOption("preset", "ultrafast");
recorder.setVideoOption("tune", "zerolatency"); recorder.setVideoOption("tune", "zerolatency");
@ -107,6 +117,14 @@ public class ConverterTranFactories extends Thread implements Converter {
while (runing) { while (runing) {
// 抓取一帧 // 抓取一帧
Frame f = grabber.grab(); Frame f = grabber.grab();
if(openFLVService!=null&&openFLVService.openPreview()){
//预处理
IplImage iplImage = converter.convert(f);//抓取一帧视频并将其转换为图像至于用这个图像用来做什么加水印人脸识别等等自行添加
if (iplImage != null) {
openFLVService.preview(iplImage);
f = converter.convert(iplImage);
}
}
if (f != null) { if (f != null) {
try { try {
// 转码 // 转码

View File

@ -11,7 +11,7 @@ public interface IFLVService {
* @param url * @param url
* @param response * @param response
*/ */
public void open(Integer channel,String url, HttpServletResponse response, HttpServletRequest request); public void open(Integer channel,String url, HttpServletResponse response, HttpServletRequest request,IOpenFLVService openFLVService);
public void open(String url, HttpServletResponse response, HttpServletRequest request); public void open(String url, HttpServletResponse response, HttpServletRequest request,IOpenFLVService openFLVService);
} }

View File

@ -1,5 +1,7 @@
package com.gc.easy.flv.service; package com.gc.easy.flv.service;
import org.bytedeco.opencv.opencv_core.IplImage;
public interface IOpenFLVService { public interface IOpenFLVService {
@ -8,5 +10,17 @@ public interface IOpenFLVService {
* @param channel * @param channel
* @return * @return
*/ */
public String getUrl(Integer channel); String getUrl(Integer channel);
/**
* 抓取一帧视频并将其转换为图像预处理
* @param iplImage
*/
void preview(IplImage iplImage);
/**
* 开启预处理
* @return
*/
boolean openPreview();
} }

View File

@ -4,6 +4,7 @@ package com.gc.easy.flv.service.impl;
import com.gc.easy.flv.factories.Converter; import com.gc.easy.flv.factories.Converter;
import com.gc.easy.flv.factories.ConverterFactories; import com.gc.easy.flv.factories.ConverterFactories;
import com.gc.easy.flv.service.IFLVService; import com.gc.easy.flv.service.IFLVService;
import com.gc.easy.flv.service.IOpenFLVService;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@ -27,12 +28,14 @@ import java.util.concurrent.ConcurrentHashMap;
public class FLVService implements IFLVService { public class FLVService implements IFLVService {
private ConcurrentHashMap<String, Converter> converters = new ConcurrentHashMap<>(); private ConcurrentHashMap<String, Converter> converters = new ConcurrentHashMap<>();
@Override @Override
public void open(String url, HttpServletResponse response, HttpServletRequest request) { public void open(String url, HttpServletResponse response, HttpServletRequest request, IOpenFLVService openFLVService) {
open(null,url,response,request); open(null, url, response, request, openFLVService);
} }
@Override @Override
public void open(Integer channel,String url, HttpServletResponse response, HttpServletRequest request) { public void open(Integer channel, String url, HttpServletResponse response, HttpServletRequest request, IOpenFLVService openFLVService) {
String key = md5(url); String key = md5(url);
AsyncContext async = request.startAsync(); AsyncContext async = request.startAsync();
async.setTimeout(0); async.setTimeout(0);
@ -47,7 +50,7 @@ public class FLVService implements IFLVService {
} else { } else {
List<AsyncContext> outs = new ArrayList<>(); List<AsyncContext> outs = new ArrayList<>();
outs.add(async); outs.add(async);
ConverterFactories c = new ConverterFactories(url, key, converters, outs); ConverterFactories c = new ConverterFactories(url, key, converters, outs, openFLVService);
c.start(); c.start();
converters.put(key, c); converters.put(key, c);
} }

View File

@ -0,0 +1,33 @@
package com.gc.easy.flv.service.impl;
import com.gc.easy.flv.service.IOpenFLVService;
import lombok.extern.slf4j.Slf4j;
import org.bytedeco.opencv.opencv_core.IplImage;
import org.springframework.stereotype.Service;
/**
* FLV流转换
*
* @author gc.x
*/
@Slf4j
@Service
public class OpenFLVService implements IOpenFLVService {
@Override
public String getUrl(Integer channel) {
return "";
}
@Override
public void preview(IplImage iplImage) {
}
@Override
public boolean openPreview() {
return false;
}
}

View File

@ -0,0 +1,73 @@
package com.gc.easy.flv.util;
import com.alibaba.fastjson.JSONObject;
import org.bytedeco.javacv.FFmpegFrameGrabber;
import org.bytedeco.javacv.Frame;
import org.bytedeco.javacv.FrameGrabber;
import org.bytedeco.javacv.Java2DFrameConverter;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
public class FlvUtil {
private static BufferedImage getBufferedImageByFrame(String filePath) throws IOException {
FFmpegFrameGrabber grabber = FFmpegFrameGrabber.createDefault(filePath);
return getBufferedImageByFrame(grabber);
}
private static BufferedImage getBufferedImageByFrame(FFmpegFrameGrabber grabber)
throws FrameGrabber.Exception {
grabber.start();
Frame frame;
frame = grabber.grabImage();
Java2DFrameConverter converter = new Java2DFrameConverter();
BufferedImage buffer = converter.getBufferedImage(frame);
grabber.stop();
return buffer;
}
public static byte[] getFlvImg(String path) throws Exception {
return bufferedImageToByteArray(getBufferedImageByFrame(path));
}
/**
* 将BufferedImage转换为byte[]
*
* @param image
* @return
*/
public static byte[] bufferedImageToByteArray(BufferedImage image) throws IOException {
ByteArrayOutputStream os = new ByteArrayOutputStream();
ImageIO.write(image, "jpg", os);
return os.toByteArray();
}
/**
* 上传文件
*
* @param data 文件数据
* @param url 上传地址
* @param fileName 文件名称
* @return
*/
public static JSONObject postFile(byte[] data, String url, String fileName) {
RestTemplate restTemplate = new RestTemplate();
MultiValueMap<String, Object> paramMap = new LinkedMultiValueMap<>();
ByteArrayResource contentsAsResource = new ByteArrayResource(data) {
@Override
public String getFilename() {
return fileName;
}
};
paramMap.add("file", contentsAsResource);
return restTemplate.postForObject(url, paramMap, JSONObject.class);
}
}

View File

@ -5,12 +5,12 @@
<title>FLV Video Player</title> <title>FLV Video Player</title>
<!-- 引入flv.js库 --> <!-- 引入flv.js库 -->
<script src="https://cdn.jsdelivr.net/npm/flv.js@1.5.0/dist/flv.min.js"></script> <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/flv.js/1.5.0/flv.min.js"></script>
</head> </head>
<body> <body>
<video id="videoPlayer" th:style="'width:' + ${width} + 'px; height:' + ${height} + 'px;'" controls></video> <video id="videoPlayer" th:style="'width:500px; height:500px;'" controls></video>
<script th:inline="javascript"> <script th:inline="javascript">
(function () { (function () {
@ -20,7 +20,7 @@
var videoElement = document.getElementById('videoPlayer'); var videoElement = document.getElementById('videoPlayer');
var flvPlayer = flvjs.createPlayer({ var flvPlayer = flvjs.createPlayer({
type: 'flv', type: 'flv',
url: videoPath url: 'http://localhost:8000' + videoPath
}); });
flvPlayer.attachMediaElement(videoElement); flvPlayer.attachMediaElement(videoElement);

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,37 @@
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>FLV Video Player</title>
<!-- 引入flv.js库 -->
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/flv.js/1.5.0/flv.min.js"></script>
</head>
<body>
<video id="videoPlayer" th:style="'width:500px; height:500px;'" controls></video>
<script th:inline="javascript">
(function () {
var videoPath = /*[[${videoPath}]]*/ '';
if (flvjs.isSupported()) {
var videoElement = document.getElementById('videoPlayer');
var flvPlayer = flvjs.createPlayer({
type: 'flv',
url: 'http://localhost:8000' + videoPath
});
flvPlayer.attachMediaElement(videoElement);
flvPlayer.load();
// 播放视频
videoElement.play();
} else {
console.error('FLV not supported');
}
})();
</script>
</body>
</html>