一.服务代码 目录结构 Maven配置文件引入坐标: org.bytedeco javacv-platfORM
org.bytedeco javacv-platfORM 1.5.1 javax.xml.bind jaxb-api 2.3.0
controller层:
import com.xr.WEB.rtspconverterflvSpringBootstarter.service.IFLVService;import io.swagger.annotations.Api;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;import javax.servlet.Http.httpservletRequest;import javax.servlet.http.HttpServletResponse;@Api(tags = "flv")@RequestMapping("/flv")@RestControllerpublic class FLVController { @Autowired private IFLVService service; @GetMapping() public void open4(HttpServletResponse response, HttpServletRequest request) { String test = "rtsp://admin:sdxr@2022@192.168.0.205:554"; service.open(test, response, request); }}
config层:
import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.scheduling.TaskScheduler;import org.springframework.scheduling.annotation.EnableScheduling;import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;@Configuration@EnableSchedulingpublic class SchedulerConfig { @Bean public TaskScheduler taskScheduler() { ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler(); // 线程池大小 scheduler.setPoolSize(3); // 线程名字前缀 scheduler.setThreadNamePrefix("task-thread-"); return scheduler; }}
factories层:
public enum ConverterState { INITIAL, OPEN, CLOSE, ERROR, RUN}
import javax.servlet.AsyncContext;import java.io.IOException;public interface Converter { public String geTKEy(); public String getUrl(); public void addOutputStreamEntity(String key, AsyncContext entity) throws IOException; public void exit(); public void start();}
import com.alibaba.fastJSON.util.IOUtils;import lombok.extern.slf4j.Slf4j;import org.bytedeco.FFmpeg.avcodec.AVPacket;import org.bytedeco.ffmpeg.global.avcodec;import org.bytedeco.javacv.FFmpegFrameGrabber;import org.bytedeco.javacv.FFmpegFrameRecorder;import javax.servlet.AsyncContext;import java.io.ByteArrayOutputStream;import java.io.IOException;import java.util.Iterator;import java.util.List;import java.util.Map;@Slf4jpublic class ConverterFactories extends Thread implements Converter { public volatile boolean runing = true; private FFmpegFrameGrabber grabber; private FFmpegFrameRecorder recorder; private byte[] headers; private ByteArrayOutputStream stream; private String url; private List outEntitys; private String key; private Map factories; public ConverterFactories(String url, String key, Map factories, List outEntitys) { this.url = url; this.key = key; this.factories = factories; this.outEntitys = outEntitys; } @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("stimeout", "5000000"); } grabber.start(); if (avcodec.AV_CODEC_ID_H264 == grabber.getVideoCodec() && (grabber.getAudioChannels() == 0 || avcodec.AV_CODEC_ID_AAC == grabber.getAudioCodec())) { 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); 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); } } 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 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 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 exit() { this.runing = false; try { this.join(); } catch (Exception e) { log.error(e.getMessage(), e); } }}
import com.alibaba.fastjson.util.IOUtils;import lombok.extern.slf4j.Slf4j;import org.bytedeco.ffmpeg.global.avcodec;import org.bytedeco.javacv.FFmpegFrameGrabber;import org.bytedeco.javacv.FFmpegFrameRecorder;import org.bytedeco.javacv.Frame;import javax.servlet.AsyncContext;import java.io.ByteArrayOutputStream;import java.io.IOException;import java.util.Iterator;import java.util.List;import java.util.Map;@Slf4jpublic class ConverterTranFactories extends Thread implements Converter { public volatile boolean runing = true; private FFmpegFrameGrabber grabber; private FFmpegFrameRecorder recorder; private byte[] headers; private ByteArrayOutputStream stream; private String url; private List outEntitys; private String key; private Map factories; public ConverterTranFactories(String url, String key, Map factories, List outEntitys, FFmpegFrameGrabber grabber) { this.url = url; this.key = key; this.factories = factories; this.outEntitys = outEntitys; this.grabber = grabber; } @Override public void run() { try { log.info("this url:{} converterTranFactories start", url); grabber.setFrameRate(25); if (grabber.getImageWidth() > 1920) { grabber.setImageWidth(1920); } if (grabber.getImageHeight() > 1080) { grabber.setImageHeight(1080); } 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.setGopSize(50); recorder.setFrameRate(25); recorder.setSampleRate(grabber.getSampleRate()); if (grabber.getAudioChannels() > 0) { recorder.setAudioChannels(grabber.getAudioChannels()); recorder.setAudioBitrate(grabber.getAudioBitrate()); recorder.setAudioCodec(avcodec.AV_CODEC_ID_AAC); } recorder.setFormat("flv"); recorder.setVideoBitrate(grabber.getVideoBitrate()); recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264); recorder.start(); if (headers == null) { headers = stream.toByteArray(); stream.reset(); writeResponse(headers); } int nullNumber = 0; while (runing) { // 抓取一帧 Frame f = grabber.grab(); if (f != null) { try { // 转码 recorder.record(f); } catch (Exception e) { } if (stream.size() > 0) { byte[] b = stream.toByteArray(); stream.reset(); writeResponse(b); if (outEntitys.isEmpty()) {log.info("没有输出退出");break; } } } else { nullNumber++; if (nullNumber > 200) { break; } } Thread.sleep(5); } } catch (Exception e) { log.error(e.getMessage(), e); } finally { closeConverter(); completeResponse(); log.info("this url:{} converterTranFactories exit", url); factories.remove(this.key); } } 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() { IOUtils.close(grabber); IOUtils.close(recorder); IOUtils.close(stream); } public void completeResponse() { Iterator it = outEntitys.iterator(); while (it.hasNext()) { AsyncContext o = it.next(); o.complete(); } } @Override public String getKey() { return this.key; } @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 exit() { this.runing = false; try { this.join(); } catch (Exception e) { log.error(e.getMessage(), e); } }}
result层:
import com.alibaba.fastjson.JSONObject;import java.io.Serializable;import java.util.HashMap;public class JsonResult extends HashMap implements Serializable { private static final long serialVersionUID = 1L; public static final int SUCCESS = 200; public JsonResult() { } public static JsonResult ok() { return ok("操作成功"); } public static JsonResult okFallBack() { return okFallBack("操作成功"); } public JsonResult put(Object obj) { return this.put("data", obj); } public static JsonResult ok(String message) { return result(200, message); } public static JsonResult okFallBack(String message) { return result(205, message); } public static JsonResult result(int code, String message) { JsonResult jsonResult = new JsonResult(); jsonResult.put("timestamp", System.currentTimeMillis()); jsonResult.put("status", code); jsonResult.put("message", message); return jsonResult; } public static JsonResult error() { return error("操作失败"); } public static JsonResult error(String message) { return error(500, message); } public static JsonResult error(int code, String message) { JsonResult jsonResult = new JsonResult(); jsonResult.put("timestamp", System.currentTimeMillis()); jsonResult.put("status", code); jsonResult.put("message", message); return jsonResult; } public JsonResult setCode(int code) { super.put("status", code); return this; } public JsonResult setMessage(String message) { super.put("message", message); return this; } @Override public JsonResult put(String key, Object object) { super.put(key, object); return this; } public static JsonResult forbidden(String message) { JsonResult jsonResult = new JsonResult(); jsonResult.put("timestamp", System.currentTimeMillis()); jsonResult.put("status", 401); jsonResult.put("message", message); return jsonResult; } @Override public String toString() { return JSONObject.toJSONString(this); } public JSONObject toJSONObject() { return JSONObject.parseObject(toString()); }}
service层:
import com.alibaba.fastjson.JSONObject;import java.io.Serializable;import java.util.HashMap;public class JsonResult extends HashMap implements Serializable { private static final long serialVersionUID = 1L; public static final int SUCCESS = 200; public JsonResult() { } public static JsonResult ok() { return ok("操作成功"); } public static JsonResult okFallBack() { return okFallBack("操作成功"); } public JsonResult put(Object obj) { return this.put("data", obj); } public static JsonResult ok(String message) { return result(200, message); } public static JsonResult okFallBack(String message) { return result(205, message); } public static JsonResult result(int code, String message) { JsonResult jsonResult = new JsonResult(); jsonResult.put("timestamp", System.currentTimeMillis()); jsonResult.put("status", code); jsonResult.put("message", message); return jsonResult; } public static JsonResult error() { return error("操作失败"); } public static JsonResult error(String message) { return error(500, message); } public static JsonResult error(int code, String message) { JsonResult jsonResult = new JsonResult(); jsonResult.put("timestamp", System.currentTimeMillis()); jsonResult.put("status", code); jsonResult.put("message", message); return jsonResult; } public JsonResult setCode(int code) { super.put("status", code); return this; } public JsonResult setMessage(String message) { super.put("message", message); return this; } @Override public JsonResult put(String key, Object object) { super.put(key, object); return this; } public static JsonResult forbidden(String message) { JsonResult jsonResult = new JsonResult(); jsonResult.put("timestamp", System.currentTimeMillis()); jsonResult.put("status", 401); jsonResult.put("message", message); return jsonResult; } @Override public String toString() { return JSONObject.toJSONString(this); } public JSONObject toJSONObject() { return JSONObject.parseObject(toString()); }}
import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;public interface IFLVService { public void open(String url, HttpServletResponse response, HttpServletRequest request);}
Document
这里因为浏览器把自动播放给禁止了,加了个按钮点击事件
引入的flv.js文件在如下网站下载即可:
来源地址:https://blog.csdn.net/seeblood/article/details/128964479
--结束END--
本文标题: java 实现监控rtsp流转flv,实现前端播放(前后端代码都有)
本文链接: https://lsjlt.com/news/389699.html(转载时请注明来源链接)
有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341
2024-04-01
2024-04-03
2024-04-03
2024-01-21
2024-01-21
2024-01-21
2024-01-21
2023-12-23
回答
回答
回答
回答
回答
回答
回答
回答
回答
回答
0