diff --git a/.gitignore b/.gitignore
deleted file mode 100644
index 21b477f..0000000
--- a/.gitignore
+++ /dev/null
@@ -1,33 +0,0 @@
-README.cn.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/
diff --git a/LICENSE b/LICENSE
deleted file mode 100644
index 261eeb9..0000000
--- a/LICENSE
+++ /dev/null
@@ -1,201 +0,0 @@
- Apache License
- Version 2.0, January 2004
- http://www.apache.org/licenses/
-
- TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
- 1. Definitions.
-
- "License" shall mean the terms and conditions for use, reproduction,
- and distribution as defined by Sections 1 through 9 of this document.
-
- "Licensor" shall mean the copyright owner or entity authorized by
- the copyright owner that is granting the License.
-
- "Legal Entity" shall mean the union of the acting entity and all
- other entities that control, are controlled by, or are under common
- control with that entity. For the purposes of this definition,
- "control" means (i) the power, direct or indirect, to cause the
- direction or management of such entity, whether by contract or
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
- outstanding shares, or (iii) beneficial ownership of such entity.
-
- "You" (or "Your") shall mean an individual or Legal Entity
- exercising permissions granted by this License.
-
- "Source" form shall mean the preferred form for making modifications,
- including but not limited to software source code, documentation
- source, and configuration files.
-
- "Object" form shall mean any form resulting from mechanical
- transformation or translation of a Source form, including but
- not limited to compiled object code, generated documentation,
- and conversions to other media types.
-
- "Work" shall mean the work of authorship, whether in Source or
- Object form, made available under the License, as indicated by a
- copyright notice that is included in or attached to the work
- (an example is provided in the Appendix below).
-
- "Derivative Works" shall mean any work, whether in Source or Object
- form, that is based on (or derived from) the Work and for which the
- editorial revisions, annotations, elaborations, or other modifications
- represent, as a whole, an original work of authorship. For the purposes
- of this License, Derivative Works shall not include works that remain
- separable from, or merely link (or bind by name) to the interfaces of,
- the Work and Derivative Works thereof.
-
- "Contribution" shall mean any work of authorship, including
- the original version of the Work and any modifications or additions
- to that Work or Derivative Works thereof, that is intentionally
- submitted to Licensor for inclusion in the Work by the copyright owner
- or by an individual or Legal Entity authorized to submit on behalf of
- the copyright owner. For the purposes of this definition, "submitted"
- means any form of electronic, verbal, or written communication sent
- to the Licensor or its representatives, including but not limited to
- communication on electronic mailing lists, source code control systems,
- and issue tracking systems that are managed by, or on behalf of, the
- Licensor for the purpose of discussing and improving the Work, but
- excluding communication that is conspicuously marked or otherwise
- designated in writing by the copyright owner as "Not a Contribution."
-
- "Contributor" shall mean Licensor and any individual or Legal Entity
- on behalf of whom a Contribution has been received by Licensor and
- subsequently incorporated within the Work.
-
- 2. Grant of Copyright License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- copyright license to reproduce, prepare Derivative Works of,
- publicly display, publicly perform, sublicense, and distribute the
- Work and such Derivative Works in Source or Object form.
-
- 3. Grant of Patent License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- (except as stated in this section) patent license to make, have made,
- use, offer to sell, sell, import, and otherwise transfer the Work,
- where such license applies only to those patent claims licensable
- by such Contributor that are necessarily infringed by their
- Contribution(s) alone or by combination of their Contribution(s)
- with the Work to which such Contribution(s) was submitted. If You
- institute patent litigation against any entity (including a
- cross-claim or counterclaim in a lawsuit) alleging that the Work
- or a Contribution incorporated within the Work constitutes direct
- or contributory patent infringement, then any patent licenses
- granted to You under this License for that Work shall terminate
- as of the date such litigation is filed.
-
- 4. Redistribution. You may reproduce and distribute copies of the
- Work or Derivative Works thereof in any medium, with or without
- modifications, and in Source or Object form, provided that You
- meet the following conditions:
-
- (a) You must give any other recipients of the Work or
- Derivative Works a copy of this License; and
-
- (b) You must cause any modified files to carry prominent notices
- stating that You changed the files; and
-
- (c) You must retain, in the Source form of any Derivative Works
- that You distribute, all copyright, patent, trademark, and
- attribution notices from the Source form of the Work,
- excluding those notices that do not pertain to any part of
- the Derivative Works; and
-
- (d) If the Work includes a "NOTICE" text file as part of its
- distribution, then any Derivative Works that You distribute must
- include a readable copy of the attribution notices contained
- within such NOTICE file, excluding those notices that do not
- pertain to any part of the Derivative Works, in at least one
- of the following places: within a NOTICE text file distributed
- as part of the Derivative Works; within the Source form or
- documentation, if provided along with the Derivative Works; or,
- within a display generated by the Derivative Works, if and
- wherever such third-party notices normally appear. The contents
- of the NOTICE file are for informational purposes only and
- do not modify the License. You may add Your own attribution
- notices within Derivative Works that You distribute, alongside
- or as an addendum to the NOTICE text from the Work, provided
- that such additional attribution notices cannot be construed
- as modifying the License.
-
- You may add Your own copyright statement to Your modifications and
- may provide additional or different license terms and conditions
- for use, reproduction, or distribution of Your modifications, or
- for any such Derivative Works as a whole, provided Your use,
- reproduction, and distribution of the Work otherwise complies with
- the conditions stated in this License.
-
- 5. Submission of Contributions. Unless You explicitly state otherwise,
- any Contribution intentionally submitted for inclusion in the Work
- by You to the Licensor shall be under the terms and conditions of
- this License, without any additional terms or conditions.
- Notwithstanding the above, nothing herein shall supersede or modify
- the terms of any separate license agreement you may have executed
- with Licensor regarding such Contributions.
-
- 6. Trademarks. This License does not grant permission to use the trade
- names, trademarks, service marks, or product names of the Licensor,
- except as required for reasonable and customary use in describing the
- origin of the Work and reproducing the content of the NOTICE file.
-
- 7. Disclaimer of Warranty. Unless required by applicable law or
- agreed to in writing, Licensor provides the Work (and each
- Contributor provides its Contributions) on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
- implied, including, without limitation, any warranties or conditions
- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
- PARTICULAR PURPOSE. You are solely responsible for determining the
- appropriateness of using or redistributing the Work and assume any
- risks associated with Your exercise of permissions under this License.
-
- 8. Limitation of Liability. In no event and under no legal theory,
- whether in tort (including negligence), contract, or otherwise,
- unless required by applicable law (such as deliberate and grossly
- negligent acts) or agreed to in writing, shall any Contributor be
- liable to You for damages, including any direct, indirect, special,
- incidental, or consequential damages of any character arising as a
- result of this License or out of the use or inability to use the
- Work (including but not limited to damages for loss of goodwill,
- work stoppage, computer failure or malfunction, or any and all
- other commercial damages or losses), even if such Contributor
- has been advised of the possibility of such damages.
-
- 9. Accepting Warranty or Additional Liability. While redistributing
- the Work or Derivative Works thereof, You may choose to offer,
- and charge a fee for, acceptance of support, warranty, indemnity,
- or other liability obligations and/or rights consistent with this
- License. However, in accepting such obligations, You may act only
- on Your own behalf and on Your sole responsibility, not on behalf
- of any other Contributor, and only if You agree to indemnify,
- defend, and hold each Contributor harmless for any liability
- incurred by, or claims asserted against, such Contributor by reason
- of your accepting any such warranty or additional liability.
-
- END OF TERMS AND CONDITIONS
-
- APPENDIX: How to apply the Apache License to your work.
-
- To apply the Apache License to your work, attach the following
- boilerplate notice, with the fields enclosed by brackets "[]"
- replaced with your own identifying information. (Don't include
- the brackets!) The text should be enclosed in the appropriate
- comment syntax for the file format. We also recommend that a
- file or class name and description of purpose be included on the
- same "printed page" as the copyright notice for easier
- identification within third-party archives.
-
- Copyright [yyyy] [name of copyright owner]
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
diff --git a/README.md b/README.md
deleted file mode 100644
index 8208e02..0000000
--- a/README.md
+++ /dev/null
@@ -1,91 +0,0 @@
-
-# 📺 Easy-FLV: Java RTSP/RTMP to FLV Converter
-
-[](https://github.com/javpower/easy-flv)
-[](https://github.com/javpower/easy-flv/issues)
-[](https://opensource.org/licenses/Apache-2.0)
-[](https://adoptopenjdk.net/)
-[](https://spring.io/projects/spring-boot)
-
-## 🌟 About Easy-FLV
-Easy-FLV is a Java library that converts RTSP or RTMP video streams into FLV format for playback in web browsers. It provides an efficient, stable, and easily integrable solution for real-time video monitoring, live streaming, and video stream processing.
-
-### Why Choose Easy-FLV?
-- **Efficient Conversion**: Quickly converts video streams to FLV format with no complex configuration required.
-- **Easy Integration**: Used as a Spring Boot Starter, it can be easily integrated into any Java project.
-- **Modern Browser Support**: Supports all major modern browsers without the need for additional plugins.
-- **Real-time Stream Processing**: Suitable for the conversion of real-time video streams, such as security monitoring and live broadcasting.
-
-## 📄 Screenshots
-Below are screenshots of Easy-FLV in action:
-
-
-
-
-## 🚀 Quick Start
-
-### Add Maven Dependency
-Include the following Maven dependency in your Spring Boot project:
-
-```xml
-
- io.github.javpower
- rtsp-converter-flv-spring-boot-starter
- 1.5.9.1
-
-```
-
-### Implement Interface
-Create a service class that implements the `IOpenFLVService` interface to provide the stream address:
-
-```java
-@Service
-public class RtspDataService implements IOpenFLVService {
-
- @Override
- public String getUrl(Integer channel) {
- // Retrieve the RTSP stream address based on the channel
- return "rtsp://10.11.9.251:554/openUrl/16HV8mA";
- }
-}
-```
-
-### Configure YAML
-Configure Easy-FLV in your `application.yml`:
-
-```yaml
-easy:
- flv:
- host: http://localhost:8200
-```
-
-### Use Interface
-To get the converted stream address and play it in a browser:
-
-- Conversion URL: `GET http://ip:port/get/flv/hls/stream_{channel}.flv`
-- Direct Browser Playback: `GET http://ip:port/flv/hls/stream_{channel}.flv`
-
-### Direct Usage
-If you prefer not to implement an interface, you can directly encode the stream address and convert it:
-
-```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);
-}
-```
-
-- Conversion URL: `GET http://ip:port/get/flv/hls/stream?url=EncodedAddress`
-- Direct Browser Playback: `GET http://ip:port/flv/hls/stream?url=EncodedAddress`
-
-## 🛠️ Contribution
-Contributions of any kind are welcome, including but not limited to reporting bugs, submitting fixes, adding new features, and improving documentation.
-
-## 📄 License
-Easy-FLV is released under the [Apache License 2.0](LICENSE).
-
-## 📧 Contact
-- Email: [javpower@163.com](mailto:javpower@163.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)
diff --git a/img.png b/img.png
deleted file mode 100644
index e67c5c2..0000000
Binary files a/img.png and /dev/null differ
diff --git a/img_1.png b/img_1.png
deleted file mode 100644
index 1989444..0000000
Binary files a/img_1.png and /dev/null differ
diff --git a/pom.xml b/pom.xml
index a9b9a48..d6c6872 100644
--- a/pom.xml
+++ b/pom.xml
@@ -2,12 +2,12 @@
4.0.0
- io.github.javpower
- rtsp-converter-flv-spring-boot-starter
- 1.5.9.1
- rtsp-converter-flv-spring-boot-starter
- a tool about easy rtsp-converter-flv
- https://github.com/javpower/easy-flv
+ me.zhengjie
+ eladmin-flv
+ 1.1
+ 视频模块
+ 视频流转换 rtsp/rtmp 转flv
+
The Apache Software License, Version 2.0
@@ -38,6 +38,11 @@
+
+ me.zhengjie
+ eladmin-logging
+ 1.1
+
org.springframework.boot
spring-boot-starter
diff --git a/src/main/java/com/gc/easy/flv/controller/FLVController.java b/src/main/java/com/gc/easy/flv/controller/FLVController.java
index a89dad4..0b01b25 100644
--- a/src/main/java/com/gc/easy/flv/controller/FLVController.java
+++ b/src/main/java/com/gc/easy/flv/controller/FLVController.java
@@ -2,10 +2,16 @@ package com.gc.easy.flv.controller;
import com.gc.easy.flv.service.IFLVService;
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.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
@@ -14,31 +20,28 @@ import java.io.UnsupportedEncodingException;
/**
* FLV流转换
- *
+ *
* @author gc.x
*/
-@RestController
+@Slf4j
+@RestController("api/front/flv")
+@Api(tags = "微信:拉流播放")
public class FLVController {
- @Autowired
- private IFLVService service;
- @Autowired(required = false)
- private IOpenFLVService openFLVService;
+ @Autowired
+ private IFLVService service;
- @GetMapping(value = "/get/flv/hls/stream_{channel}.flv")
- public void open(@PathVariable(value = "channel") Integer channel, HttpServletResponse response,
- HttpServletRequest request) {
- String url = openFLVService.getUrl(channel);
- if(!StringUtils.isEmpty(url)){
- service.open(channel,url, response, request,openFLVService);
- }
- }
- @GetMapping(value = "/get/flv/hls/stream")
- public void open1(String url, HttpServletResponse response,
- HttpServletRequest request) throws UnsupportedEncodingException {
- if(!StringUtils.isEmpty(url)){
- String decodedUrl = java.net.URLDecoder.decode(url, "UTF-8");
- service.open(decodedUrl, response, request,openFLVService);
- }
- }
+ @Autowired(required = false)
+ private IOpenFLVService openFLVService;
+
+ @Log("拉流播放")
+ @ApiOperation(value = "打开视频流")
+ // @GetMapping(value = "/get/stream")
+ @AnonymousGetMapping(value = "/get/stream")
+ public void open(String url, HttpServletResponse response, HttpServletRequest request) throws UnsupportedEncodingException {
+ if (!StringUtils.isEmpty(url)) {
+ String decodedUrl = java.net.URLDecoder.decode(url, "UTF-8");
+ service.open(decodedUrl, response, request, openFLVService);
+ }
+ }
}
diff --git a/src/main/java/com/gc/easy/flv/controller/FLVPlayController.java b/src/main/java/com/gc/easy/flv/controller/FLVPlayController.java
index 6ebcd92..3bded18 100644
--- a/src/main/java/com/gc/easy/flv/controller/FLVPlayController.java
+++ b/src/main/java/com/gc/easy/flv/controller/FLVPlayController.java
@@ -1,39 +1,30 @@
-package com.gc.easy.flv.controller;
-
-import com.gc.easy.flv.config.FlvConfig;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Controller;
-import org.springframework.ui.Model;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.PathVariable;
-
-import java.io.UnsupportedEncodingException;
-
-/**
- * FLV流转换
- *
- * @author gc.x
- */
-@Controller
-public class FLVPlayController {
- @Autowired
- private FlvConfig flvConfig;
-
- @GetMapping(value = "/flv/hls/stream_{channel}.flv")
- public String getAppHtml(@PathVariable(value = "channel") Integer channel, Model model) {
- String videoPath=flvConfig.getHost()+"/get/flv/hls/stream_"+channel+".flv";
- model.addAttribute("videoPath", videoPath);
- model.addAttribute("wight", flvConfig.getWight());
- model.addAttribute("height", flvConfig.getHeight());
- 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";
- }
-}
+//package com.gc.easy.flv.controller;
+//
+//import com.gc.easy.flv.config.FlvConfig;
+//import me.zhengjie.annotation.rest.AnonymousGetMapping;
+//import org.springframework.beans.factory.annotation.Autowired;
+//import org.springframework.stereotype.Controller;
+//import org.springframework.ui.Model;
+//
+//import java.io.UnsupportedEncodingException;
+//
+///**
+// * FLV流转换
+// *
+// * @author gc.x
+// */
+//@Controller
+//public class FLVPlayController {
+// @Autowired
+// private FlvConfig flvConfig;
+//
+// @AnonymousGetMapping(value = "/flv/hls/stream")
+// public String getAppHtml1(String url, Model model) throws UnsupportedEncodingException {
+// String decodedUrl = java.net.URLDecoder.decode(url, "UTF-8");
+// String videoPath="/api/front/flv/get/stream?url="+decodedUrl;
+// model.addAttribute("videoPath", videoPath);
+// model.addAttribute("wight", flvConfig.getWight());
+// model.addAttribute("height", flvConfig.getHeight());
+// return "video";
+// }
+//}
diff --git a/src/main/java/com/gc/easy/flv/factories/ConverterFactories.java b/src/main/java/com/gc/easy/flv/factories/ConverterFactories.java
index 97ea29c..91081c4 100644
--- a/src/main/java/com/gc/easy/flv/factories/ConverterFactories.java
+++ b/src/main/java/com/gc/easy/flv/factories/ConverterFactories.java
@@ -24,209 +24,209 @@ import java.util.Map;
*/
@Slf4j
public class ConverterFactories extends Thread implements Converter {
- public volatile boolean runing = true;
- /**
- * 读流器
- */
- private FFmpegFrameGrabber grabber;
- /**
- * 转码器
- */
- private FFmpegFrameRecorder recorder;
- /**
- * 转FLV格式的头信息
- * 如果有第二个客户端播放首先要返回头信息
- */
- private byte[] headers;
- /**
- * 保存转换好的流
- */
- private ByteArrayOutputStream stream;
- /**
- * 流地址,h264,aac
- */
- private String url;
- /**
- * 流输出
- */
- private List outEntitys;
+ public volatile boolean runing = true;
+ /**
+ * 读流器
+ */
+ private FFmpegFrameGrabber grabber;
+ /**
+ * 转码器
+ */
+ private FFmpegFrameRecorder recorder;
+ /**
+ * 转FLV格式的头信息
+ * 如果有第二个客户端播放首先要返回头信息
+ */
+ private byte[] headers;
+ /**
+ * 保存转换好的流
+ */
+ private ByteArrayOutputStream stream;
+ /**
+ * 流地址,h264,aac
+ */
+ private String url;
+ /**
+ * 流输出
+ */
+ private List outEntitys;
- /**
- * key用于表示这个转换器
- */
- private String key;
+ /**
+ * key用于表示这个转换器
+ */
+ private String key;
- /**
- * 转换队列
- */
- private Map factories;
+ /**
+ * 转换队列
+ */
+ private Map factories;
- private IOpenFLVService openFLVService;
+ private IOpenFLVService openFLVService;
- public ConverterFactories(String url, String key, Map factories, List outEntitys, IOpenFLVService openFLVService) {
- this.url = url;
- this.key = key;
- this.factories = factories;
- this.outEntitys = outEntitys;
- this.openFLVService=openFLVService;
- }
+ public ConverterFactories(String url, String key, Map factories, List outEntitys, IOpenFLVService openFLVService) {
+ this.url = url;
+ this.key = key;
+ this.factories = factories;
+ this.outEntitys = outEntitys;
+ this.openFLVService = openFLVService;
+ }
- @Override
- public void run() {
- boolean isCloseGrabberAndResponse = true;
- try {
- grabber = new FFmpegFrameGrabber(url);
- if ("rtsp".equals(url.substring(0, 4))) {
- grabber.setOption("rtsp_transport", "tcp");
- grabber.setOption("timeout", "5000000");
- }
- grabber.start();
- if (avcodec.AV_CODEC_ID_H264 == grabber.getVideoCodec()
- && (grabber.getAudioChannels() == 0 || avcodec.AV_CODEC_ID_AAC == grabber.getAudioCodec())&&(openFLVService==null||!openFLVService.openPreview())) {
- log.info("this url:{} converterFactories start", url);
- // 来源视频H264格式,音频AAC格式
- // 无须转码,更低的资源消耗,更低的延迟
- stream = new ByteArrayOutputStream();
- recorder = new FFmpegFrameRecorder(stream, grabber.getImageWidth(), grabber.getImageHeight(),
- grabber.getAudioChannels());
- recorder.setInterleaved(true);
- recorder.setVideoOption("preset", "ultrafast");
- recorder.setVideoOption("tune", "zerolatency");
- recorder.setVideoOption("crf", "25");
- recorder.setFrameRate(grabber.getFrameRate());
- recorder.setSampleRate(grabber.getSampleRate());
- if (grabber.getAudioChannels() > 0) {
- recorder.setAudioChannels(grabber.getAudioChannels());
- recorder.setAudioBitrate(grabber.getAudioBitrate());
- recorder.setAudioCodec(grabber.getAudioCodec());
- }
- recorder.setFormat("flv");
- recorder.setVideoBitrate(grabber.getVideoBitrate());
- recorder.setVideoCodec(grabber.getVideoCodec());
- recorder.start(grabber.getFormatContext());
- if (headers == null) {
- headers = stream.toByteArray();
- stream.reset();
- writeResponse(headers);
- }
- int nullNumber = 0;
- while (runing) {
- AVPacket k = grabber.grabPacket();
- if (k != null) {
- try {
- recorder.recordPacket(k);
- } catch (Exception e) {
- }
- if (stream.size() > 0) {
- byte[] b = stream.toByteArray();
- stream.reset();
- writeResponse(b);
- if (outEntitys.isEmpty()) {
- log.info("没有输出退出");
- break;
- }
- }
- avcodec.av_packet_unref(k);
- } else {
- nullNumber++;
- if (nullNumber > 200) {
- break;
- }
- }
- Thread.sleep(5);
- }
- } else {
- isCloseGrabberAndResponse = false;
- // 需要转码为视频H264格式,音频AAC格式
- ConverterTranFactories c = new ConverterTranFactories(url, key, factories, outEntitys, grabber,openFLVService);
- factories.put(key, c);
- c.start();
- }
- } catch (Exception e) {
- log.error(e.getMessage(), e);
- } finally {
- closeConverter(isCloseGrabberAndResponse);
- completeResponse(isCloseGrabberAndResponse);
- log.info("this url:{} converterFactories exit", url);
+ @Override
+ public void run() {
+ boolean isCloseGrabberAndResponse = true;
+ try {
+ grabber = new FFmpegFrameGrabber(url);
+ if ("rtsp".equals(url.substring(0, 4))) {
+ grabber.setOption("rtsp_transport", "tcp");
+ grabber.setOption("timeout", "5000000");
+ }
+ grabber.start();
+ if (avcodec.AV_CODEC_ID_H264 == grabber.getVideoCodec()
+ && (grabber.getAudioChannels() == 0 || avcodec.AV_CODEC_ID_AAC == grabber.getAudioCodec()) && (openFLVService == null || !openFLVService.openPreview())) {
+ log.info("this url:{} converterFactories start", url);
+ // 来源视频H264格式,音频AAC格式
+ // 无须转码,更低的资源消耗,更低的延迟
+ stream = new ByteArrayOutputStream();
+ recorder = new FFmpegFrameRecorder(stream, grabber.getImageWidth(), grabber.getImageHeight(),
+ grabber.getAudioChannels());
+ recorder.setInterleaved(true);
+ recorder.setVideoOption("preset", "ultrafast");
+ recorder.setVideoOption("tune", "zerolatency");
+ recorder.setVideoOption("crf", "25");
+ recorder.setFrameRate(grabber.getFrameRate());
+ recorder.setSampleRate(grabber.getSampleRate());
+ if (grabber.getAudioChannels() > 0) {
+ recorder.setAudioChannels(grabber.getAudioChannels());
+ recorder.setAudioBitrate(grabber.getAudioBitrate());
+ recorder.setAudioCodec(grabber.getAudioCodec());
+ }
+ recorder.setFormat("flv");
+ recorder.setVideoBitrate(grabber.getVideoBitrate());
+ recorder.setVideoCodec(grabber.getVideoCodec());
+ recorder.start(grabber.getFormatContext());
+ if (headers == null) {
+ headers = stream.toByteArray();
+ stream.reset();
+ writeResponse(headers);
+ }
+ int nullNumber = 0;
+ while (runing) {
+ AVPacket k = grabber.grabPacket();
+ if (k != null) {
+ try {
+ recorder.recordPacket(k);
+ } catch (Exception e) {
+ }
+ if (stream.size() > 0) {
+ byte[] b = stream.toByteArray();
+ stream.reset();
+ writeResponse(b);
+ if (outEntitys.isEmpty()) {
+ log.info("没有输出退出");
+ break;
+ }
+ }
+ avcodec.av_packet_unref(k);
+ } else {
+ nullNumber++;
+ if (nullNumber > 200) {
+ break;
+ }
+ }
+ Thread.sleep(5);
+ }
+ } else {
+ isCloseGrabberAndResponse = false;
+ // 需要转码为视频H264格式,音频AAC格式
+ ConverterTranFactories c = new ConverterTranFactories(url, key, factories, outEntitys, grabber, openFLVService);
+ factories.put(key, c);
+ c.start();
+ }
+ } catch (Exception e) {
+ log.error(e.getMessage(), e);
+ } finally {
+ closeConverter(isCloseGrabberAndResponse);
+ completeResponse(isCloseGrabberAndResponse);
+ log.info("this url:{} converterFactories exit", url);
- }
- }
+ }
+ }
- /**
- * 输出FLV视频流
- *
- * @param b
- */
- public void writeResponse(byte[] b) {
- Iterator it = outEntitys.iterator();
- while (it.hasNext()) {
- AsyncContext o = it.next();
- try {
- o.getResponse().getOutputStream().write(b);
- } catch (Exception e) {
- log.info("移除一个输出");
- it.remove();
- }
- }
- }
+ /**
+ * 输出FLV视频流
+ *
+ * @param b
+ */
+ public void writeResponse(byte[] b) {
+ Iterator it = outEntitys.iterator();
+ while (it.hasNext()) {
+ AsyncContext o = it.next();
+ try {
+ o.getResponse().getOutputStream().write(b);
+ } catch (Exception e) {
+ log.info("移除一个输出");
+ it.remove();
+ }
+ }
+ }
- /**
- * 退出转换
- */
- public void closeConverter(boolean isCloseGrabberAndResponse) {
- if (isCloseGrabberAndResponse) {
- IOUtils.close(grabber);
- factories.remove(this.key);
- }
- IOUtils.close(recorder);
- IOUtils.close(stream);
- }
+ /**
+ * 退出转换
+ */
+ public void closeConverter(boolean isCloseGrabberAndResponse) {
+ if (isCloseGrabberAndResponse) {
+ IOUtils.close(grabber);
+ factories.remove(this.key);
+ }
+ IOUtils.close(recorder);
+ IOUtils.close(stream);
+ }
- /**
- * 关闭异步响应
- *
- * @param isCloseGrabberAndResponse
- */
- public void completeResponse(boolean isCloseGrabberAndResponse) {
- if (isCloseGrabberAndResponse) {
- Iterator it = outEntitys.iterator();
- while (it.hasNext()) {
- AsyncContext o = it.next();
- o.complete();
- }
- }
- }
+ /**
+ * 关闭异步响应
+ *
+ * @param isCloseGrabberAndResponse
+ */
+ public void completeResponse(boolean isCloseGrabberAndResponse) {
+ if (isCloseGrabberAndResponse) {
+ Iterator it = outEntitys.iterator();
+ while (it.hasNext()) {
+ AsyncContext o = it.next();
+ o.complete();
+ }
+ }
+ }
- @Override
- public String getKey() {
- return this.key;
- }
+ @Override
+ public String getKey() {
+ return this.key;
+ }
- @Override
- public String getUrl() {
- return this.url;
- }
+ @Override
+ public String getUrl() {
+ return this.url;
+ }
- @Override
- public void addOutputStreamEntity(String key, AsyncContext entity) throws IOException {
- if (headers == null) {
- outEntitys.add(entity);
- } else {
- entity.getResponse().getOutputStream().write(headers);
- entity.getResponse().getOutputStream().flush();
- outEntitys.add(entity);
- }
- }
+ @Override
+ public void addOutputStreamEntity(String key, AsyncContext entity) throws IOException {
+ if (headers == null) {
+ outEntitys.add(entity);
+ } else {
+ entity.getResponse().getOutputStream().write(headers);
+ entity.getResponse().getOutputStream().flush();
+ outEntitys.add(entity);
+ }
+ }
- @Override
- public void exit() {
- this.runing = false;
- try {
- this.join();
- } catch (Exception e) {
- log.error(e.getMessage(), e);
- }
- }
+ @Override
+ public void exit() {
+ this.runing = false;
+ try {
+ this.join();
+ } catch (Exception e) {
+ log.error(e.getMessage(), e);
+ }
+ }
}
diff --git a/src/main/java/com/gc/easy/flv/service/IOpenFLVService.java b/src/main/java/com/gc/easy/flv/service/IOpenFLVService.java
index 988c21a..3b53397 100644
--- a/src/main/java/com/gc/easy/flv/service/IOpenFLVService.java
+++ b/src/main/java/com/gc/easy/flv/service/IOpenFLVService.java
@@ -10,7 +10,7 @@ public interface IOpenFLVService {
* @param channel
* @return
*/
- String getUrl(Integer channel);
+ String getUrl(Integer channel);
/**
* 抓取一帧视频并将其转换为图像预处理
diff --git a/src/main/java/com/gc/easy/flv/service/impl/FLVService.java b/src/main/java/com/gc/easy/flv/service/impl/FLVService.java
index 0a25e0f..3845f01 100644
--- a/src/main/java/com/gc/easy/flv/service/impl/FLVService.java
+++ b/src/main/java/com/gc/easy/flv/service/impl/FLVService.java
@@ -20,68 +20,70 @@ import java.util.concurrent.ConcurrentHashMap;
/**
* FLV流转换
- *
+ *
* @author gc.x
*/
@Slf4j
@Service
public class FLVService implements IFLVService {
- private ConcurrentHashMap converters = new ConcurrentHashMap<>();
- @Override
- public void open(String url, HttpServletResponse response, HttpServletRequest request,IOpenFLVService openFLVService) {
- open(null,url,response,request,openFLVService);
- }
- @Override
- public void open(Integer channel, String url, HttpServletResponse response, HttpServletRequest request, IOpenFLVService openFLVService) {
- String key = md5(url);
- AsyncContext async = request.startAsync();
- async.setTimeout(0);
- if (converters.containsKey(key)) {
- Converter c = converters.get(key);
- try {
- c.addOutputStreamEntity(key, async);
- } catch (IOException e) {
- log.error(e.getMessage(), e);
- throw new IllegalArgumentException(e.getMessage());
- }
- } else {
- List outs = new ArrayList<>();
- outs.add(async);
- ConverterFactories c = new ConverterFactories(url, key, converters, outs,openFLVService);
- c.start();
- converters.put(key, c);
- }
- response.setContentType("video/x-flv");
- response.setHeader("Connection", "keep-alive");
- response.setStatus(HttpServletResponse.SC_OK);
- try {
- response.flushBuffer();
- } catch (IOException e) {
- log.error(e.getMessage(), e);
- }
- }
+ private ConcurrentHashMap converters = new ConcurrentHashMap<>();
- public String md5(String plainText) {
- StringBuilder buf = null;
- try {
- MessageDigest md = MessageDigest.getInstance("MD5");
- md.update(plainText.getBytes());
- byte b[] = md.digest();
- int i;
- buf = new StringBuilder("");
- for (int offset = 0; offset < b.length; offset++) {
- i = b[offset];
- if (i < 0)
- i += 256;
- if (i < 16)
- buf.append("0");
- buf.append(Integer.toHexString(i));
- }
- } catch (NoSuchAlgorithmException e) {
- log.error(e.getMessage(), e);
- }
- return buf.toString();
- }
+ @Override
+ public void open(String url, HttpServletResponse response, HttpServletRequest request, IOpenFLVService openFLVService) {
+ open(null, url, response, request, openFLVService);
+ }
+
+ @Override
+ public void open(Integer channel, String url, HttpServletResponse response, HttpServletRequest request, IOpenFLVService openFLVService) {
+ String key = md5(url);
+ AsyncContext async = request.startAsync();
+ async.setTimeout(0);
+ if (converters.containsKey(key)) {
+ Converter c = converters.get(key);
+ try {
+ c.addOutputStreamEntity(key, async);
+ } catch (IOException e) {
+ log.error(e.getMessage(), e);
+ throw new IllegalArgumentException(e.getMessage());
+ }
+ } else {
+ List outs = new ArrayList<>();
+ outs.add(async);
+ ConverterFactories c = new ConverterFactories(url, key, converters, outs, openFLVService);
+ c.start();
+ converters.put(key, c);
+ }
+ response.setContentType("video/x-flv");
+ response.setHeader("Connection", "keep-alive");
+ response.setStatus(HttpServletResponse.SC_OK);
+ try {
+ response.flushBuffer();
+ } catch (IOException e) {
+ log.error(e.getMessage(), e);
+ }
+ }
+
+ public String md5(String plainText) {
+ StringBuilder buf = null;
+ try {
+ MessageDigest md = MessageDigest.getInstance("MD5");
+ md.update(plainText.getBytes());
+ byte b[] = md.digest();
+ int i;
+ buf = new StringBuilder("");
+ for (int offset = 0; offset < b.length; offset++) {
+ i = b[offset];
+ if (i < 0)
+ i += 256;
+ if (i < 16)
+ buf.append("0");
+ buf.append(Integer.toHexString(i));
+ }
+ } catch (NoSuchAlgorithmException e) {
+ log.error(e.getMessage(), e);
+ }
+ return buf.toString();
+ }
}
diff --git a/src/main/java/com/gc/easy/flv/service/impl/OpenFLVService.java b/src/main/java/com/gc/easy/flv/service/impl/OpenFLVService.java
new file mode 100644
index 0000000..fdacfd0
--- /dev/null
+++ b/src/main/java/com/gc/easy/flv/service/impl/OpenFLVService.java
@@ -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;
+ }
+}
diff --git a/src/main/resources/templates/video.html b/src/main/resources/templates/video.html
index 89dd889..ab38be5 100644
--- a/src/main/resources/templates/video.html
+++ b/src/main/resources/templates/video.html
@@ -5,33 +5,33 @@
FLV Video Player
-
+
-
+
+ // 播放视频
+ videoElement.play();
+ } else {
+ console.error('FLV not supported');
+ }
+ })();
+