上一篇文章介绍了通用协议onvif获取到rtsp地址Java onvif协议通用协议获取rtsp地址

当然也有很多其他的方式获取rtsp地址

首先还是引入包:

<!--javacv--><dependency><groupId>org.bytedeco</groupId><artifactId>javacv-platform</artifactId><version>1.5.4</version></dependency>

这里我是使用的rtsp砖udp (h264)的方式推流,还有其他方式例如rtmp 或者rtp 实现方式差不多只用修改一些参数

需要用到的测试软件:VLC

这里是利用转封装的方式进行转码(由于rtsp本身就支持h264编码格式,有两种方式:1.转码2.转封装(转封装消耗的资源更少))

详细解释就不多说了,注释里面有详细说明:

/*** rtsp转 udp(转封装方式)* @author zf*/
public class RecordVideo {private FFmpegFrameGrabber grabber = null;private FFmpegFrameRecorder recorder = null;// 视频参数/*** 选择视频源* @param src* @author eguid* @throws Exception*/public RecordVideo sourcesRtsp(String src) throws Exception {// 采集/抓取器grabber = MediaUtils.createGrabber(src);grabber.start();// 开始之后ffmpeg会采集视频信息,之后就可以获取音视频信息return this;}/*** 选择输出* @param out* @author eguid* @throws IOException*/public RecordVideo target(String out) throws IOException {// 流媒体输出地址,分辨率(长,高),是否录制音频(0:不录制/1:录制) ?overrun_nonfatal=1&fifo_size=50000000//这里udp地址增加参数扩大udp缓存recorder = new FFmpegFrameRecorder(out + "?overrun_nonfatal=1&fifo_size=50000000", MediaUtils.FRAME_WIDTH, MediaUtils.FRAME_HEIGHT, 0);// 直播流格式recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264);// 降低编码延时recorder.setVideoOption("tune", "zerolatency");recorder.setMaxDelay(500);recorder.setGopSize(10);// 提升编码速度recorder.setVideoOption("preset", "ultrafast");// 录制的视频格式 flv(rtmp格式) h264(udp格式) mpegts(未压缩的udp) rawvideorecorder.setFormat("h264");// 帧数double frameLength = grabber.getLengthInFrames();long frameTime = grabber.getLengthInTime();double v = frameLength * 1000 * 1000 / frameTime;recorder.setFrameRate(v);//百度翻译的比特率,默认400000recorder.setVideoBitrate(200000);
//        recorder.setAudioOption("crf", "23");// 建议从grabber获取AudioChannels
//        recorder.setAudioChannels(grabber.getAudioChannels());
//        recorder.setInterleaved(true);// yuv420precorder.setPixelFormat(avutil.AV_PIX_FMT_YUV420P);recorder.setAudioCodec(avcodec.AV_CODEC_ID_AAC);recorder.start(grabber.getFormatContext());return this;}/*** 转封装* @author eguid* @throws IOException*/public RecordVideo go() throws IOException {System.out.println("开始推送...");long err_index = 0;//采集或推流导致的错误次数// 释放探测时缓存下来的数据帧,避免pts初始值不为0导致画面延时grabber.flush();//错误采集判断for(int no_frame_index = 0; no_frame_index < 10 || err_index > 1;) {AVPacket pkt;try {pkt = grabber.grabPacket();if(pkt == null || pkt.size() <= 0 || pkt.data() == null) {//空包记录次数跳过no_frame_index ++;continue;}//不需要编码频帧推出去err_index += (recorder.recordPacket(pkt) ? 0 : 1);//如果失败err_index自增1av_packet_unref(pkt);} catch (IOException e) {//推流失败err_index++;}}return this;}public static void main(String[] args) throws Exception, IOException {//运行,设置视频源和推流地址new RecordVideo().sourcesRtsp("rtsp://{username}:{password}@{ip}:{port}/Streaming/Unicast/channels/1602").target("udp://{ip}:{port}").go();}
}

package com.onvif.java.utils;import com.onvif.java.common.RrException;
import com.onvif.java.model.OnvifCredentials;
import com.onvif.java.service.OnvifDevice;
import org.bytedeco.ffmpeg.avcodec.AVPacket;
import org.bytedeco.ffmpeg.global.avcodec;
import org.bytedeco.ffmpeg.global.avutil;
import org.bytedeco.javacv.FFmpegFrameGrabber;
import org.bytedeco.javacv.FFmpegFrameRecorder;
import org.bytedeco.javacv.FrameGrabber;
import org.onvif.ver10.schema.GetRecordingsResponseItem;
import org.onvif.ver10.schema.Profile;
import org.onvif.ver10.schema.TransportProtocol;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;import javax.xml.soap.SOAPException;
import java.io.IOException;
import java.net.ConnectException;
import java.net.MalformedURLException;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.Date;
import java.util.List;
import java.util.concurrent.Executor;import static org.bytedeco.ffmpeg.global.avcodec.av_packet_unref;/*** @program: javaOnvif* @description: 获取rtsp地址* @author: zf* @create: 2020-09-08 10:50**/
@Component
public class MediaUtils {@AutowiredThreadPoolTaskExecutor taskExecutor;/*** 视频帧率*/public static final int FRAME_RATE = 25;/*** 视频宽度*/public static final int FRAME_WIDTH = 480;/*** 视频高度*/public static final int FRAME_HEIGHT = 270;/*** 流编码格式*/public static final int VIDEO_CODEC = avcodec.AV_CODEC_ID_H264;/*** 编码延时 zerolatency(零延迟)*/public static final String TUNE = "zerolatency";/*** 编码速度 ultrafast(极快)*/public static final String PRESET = "ultrafast";/*** 录制的视频格式 flv(rtmp格式) h264(udp格式) mpegts(未压缩的udp) rawvideo*/public static final String FORMAT = "h264";/*** 比特率*/public static final int VIDEO_BITRATE = 200000;private static FFmpegFrameGrabber grabber = null;private static FFmpegFrameRecorder recorder = null;/*** 构造视频抓取器* @param rtsp 拉流地址* @return*/public static FFmpegFrameGrabber createGrabber(String rtsp) {// 获取视频源FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(rtsp);grabber.setOption("rtsp_transport","tcp");//设置帧率grabber.setFrameRate(FRAME_RATE);//设置获取的视频宽度grabber.setImageWidth(FRAME_WIDTH);//设置获取的视频高度grabber.setImageHeight(FRAME_HEIGHT);//设置视频bit率grabber.setVideoBitrate(2000000);return grabber;}/*** 选择视频源* @param src* @author eguid* @throws FrameGrabber.Exception*/public MediaUtils from(String src) throws FrameGrabber.Exception {start = System.currentTimeMillis();// 采集/抓取器grabber = createGrabber(src);// 开始之后ffmpeg会采集视频信息grabber.start();grabber.flush();form = src.substring(src.indexOf("@") + 1);return this;}}

udp的地址是udp/h264://@{ip}:{port}

这里格式说明下:

// 录制的视频格式 flv(rtmp格式) h264(udp格式) mpegts(未压缩的udp) rawvideo
        recorder.setFormat("h264");

udp 设置的是h264 其他的根据注释选择就可以输出不同的协议,上门代码中注释部分是音频相关的如果有需要可以使用

下面是对比的海康摄像头提供的原始sdk获取和我们推流转码后的时间对比 延时在1-2秒范围内,推流稳定

再提醒下,详细参数可以根据实际情况调整,根据所需调整 例如清晰度

setVideoBitrate设置比特率 设置画面连续性setFrameRate等

排坑指南:

视频的长宽只能设置4的倍数,不然会强制使用默认

比特率的设置则需要视频源的清晰度压缩,有一个极限值越过值了再小也没用了

更新日志:

2020年11月27日

目前的所有方式中,在拉取流的时候比较耗时大约需要2到3秒的加载

在项目启动的时候提前加载一次会有比较好的效果,但是garber.start依然会消耗一定时间,根据询问大佬这个应该目前java上没有一个好的解决方案,使用c效果阔能比较好

//提前加载资源,解决第一次推流慢FFmpegFrameGrabber.tryLoad();FFmpegFrameRecorder.tryLoad();

推流方式二

直接推送图片方式(更低延迟,验证控制在1秒以内,当然也会提高一定的cpu占用量,目前i5-4590 最多推送7到8路视频流就会占用cpu80%)

ps 如果追求超低延迟可以参考

这种方式是直接抓取流帧 转换成图片直接websocket推送出去

 /*** 推送图片流* @throws Exception*/public MediaUtils startPush(String ip, Integer port) throws Exception {Long end = System.currentTimeMillis();System.out.println(form + " 开始推送 耗时:" + (end - start) + "ms");Java2DFrameConverter java2DFrameConverter = new Java2DFrameConverter();try {Frame frame;while ((frame = grabber.grabImage()) != null) {//线程控制中断if (Thread.currentThread().isInterrupted()) {System.out.println(form + " 停止推送...");return this;}BufferedImage bufferedImage = java2DFrameConverter.getBufferedImage(frame);byte[] bytes = imageToBytes(bufferedImage, "jpg");//使用udp发送图片数据udpService.sendMessageBytes(bytes, ip, port);//使用websocket发送数据
//                MyWebSocket.sendAll(channel, bytes);}} catch (Exception e){Thread.currentThread().interrupt();}finally {if (grabber != null) {grabber.stop();}}return this;}

调用方式是首先调用上面的

new MediaUtils().from(from).startPush(ip, port);

然后直接websocket推送到前端,前端只能不断替换图片即可,目前测试海康,大华和宇视延迟都在1秒以内

2021年2月7日

测试vlc 软件:

链接:百度网盘 请输入提取码 
提取码:z7ox

udpSever(Netty实现的udp服务端):

package com.onvif.java.service;import com.alibaba.fastjson.JSONObject;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.DatagramPacket;
import io.netty.channel.socket.nio.NioDatagramChannel;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.MessageToMessageEncoder;
import io.netty.util.CharsetUtil;
import lombok.Data;
import lombok.experimental.Accessors;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;import java.net.InetSocketAddress;
import java.nio.charset.StandardCharsets;
import java.util.List;/*** UDP** @author zf* @since 2019/8/16*/
@Service
public class UdpService {private static final Logger LOG = LoggerFactory.getLogger(UdpService.class);private Channel channel;private NioEventLoopGroup group;@Datapublic static class Net{private String type;private Integer tcpPort;private String udpPort;private String webPort;private String ip;}public UdpEntity start(String ip, Integer port) {group = new NioEventLoopGroup();Bootstrap bootstrap = new Bootstrap();bootstrap.group(group).channel(NioDatagramChannel.class).option(ChannelOption.SO_BROADCAST, true).option(ChannelOption.SO_BACKLOG, 1024).option(ChannelOption.SO_RCVBUF, 128 * 1024 * 1024).option(ChannelOption.SO_SNDBUF, 128 * 1024 * 1024)//增加发送长度.option(ChannelOption.RCVBUF_ALLOCATOR, new FixedRecvByteBufAllocator(128 * 1024)).handler(new ChannelInitializer<NioDatagramChannel>() {@Overrideprotected void initChannel(NioDatagramChannel socketChannel) {//解决粘包和半包问题 接收数据全部要以$next$分割socketChannel.pipeline().addLast(new DelimiterBasedFrameDecoder(128 * 1024,Unpooled.wrappedBuffer("$next$".getBytes())));socketChannel.pipeline().addLast(outboundHandler());socketChannel.pipeline().addLast(inboundHandler());}});System.out.println("###### Udp ######启动 端口:" + port);try {channel = bootstrap.bind(ip, port).sync().channel();} catch (InterruptedException e) {e.printStackTrace();}return new UdpEntity().setChannel(channel).setGroup(group);}@Data@Accessors(chain = true)public static class UdpEntity {private Channel channel;private NioEventLoopGroup group;}public static class MsgEvent {private final InetSocketAddress inetSocketAddress;public InetSocketAddress getInetSocketAddress() {return inetSocketAddress;}public String getMsg() {return msg;}private final String msg;public MsgEvent(InetSocketAddress inetSocketAddress, String msg) {this.inetSocketAddress = inetSocketAddress;this.msg = msg;}}public void sendMessageBytes(byte[] data, String ip, Integer port) {if (channel != null) {try {channel.writeAndFlush(new DatagramPacket(Unpooled.copiedBuffer(data),new InetSocketAddress(ip,port))).sync();} catch (InterruptedException e) {Thread.currentThread().interrupt();
//                e.printStackTrace();}}}/*** 关闭*/public void shutdown(){group.shutdownGracefully();}/*** 出参数据** @return*/private MessageToMessageEncoder<MsgEvent> outboundHandler() {return new MessageToMessageEncoder<MsgEvent>() {@Overrideprotected void encode(ChannelHandlerContext ctx, MsgEvent msgEvent, List<Object> out) throws Exception {ByteBuf byteBuf = ctx.alloc().buffer(msgEvent.getMsg().length());byte[] content = msgEvent.getMsg().getBytes(CharsetUtil.UTF_8);byteBuf.writeBytes(content);out.add(new DatagramPacket(byteBuf, msgEvent.getInetSocketAddress()));}};}/*** json 美化* @param json* @return*/public static String prettyJson(String json){if(StringUtils.isBlank(json)){return json;}JSONObject jsonObject;try {jsonObject = JSONObject.parseObject(json);}catch (Exception e){return json;}return JSONObject.toJSONString(jsonObject,true);}/*** 入参数据* @return*/private SimpleChannelInboundHandler<DatagramPacket> inboundHandler() {return new SimpleChannelInboundHandler<DatagramPacket>() {@Overrideprotected void channelRead0(ChannelHandlerContext ctx, DatagramPacket packet) throws Exception {ByteBuf content = packet.content();String req = content.toString(StandardCharsets.UTF_8);JSONObject object = JSONObject.parseObject(req);System.out.println("收到解析数据:" +  prettyJson(object.toJSONString()));}};}}

2020-12-21

目前发现windows server 2012R2 无法启动,经过询问javacv作者得到回复

在新版本中,目前我测过最新版本1.5.4 是无法启动,会出现无法找到acode,Could not initialize class org.bytedeco.ffmpeg.global.avutil等各种错误

作者解释是在新版本中新功能使用到了Media Foundation 的功能 且在初始化garber时会加载,若操作系统环境没有该功能则报错

解决方案1:使用1.5.1版本无问题

解决方案2:在没有Media Foundation 的系统中安装该功能,再使用最新版本

Java 视频直播JavaCV(ffmpeg h264)+RTSP实现低延时1秒推流相关推荐

  1. Java 视频转换h265、h264、mkv、mp4

    Java视频抽帧 [1. h264视频抽帧,支持windows](https://blog.csdn.net/qq_40985985/article/details/111240098) [2. h2 ...

  2. 【音视频】使用FFMPEG进行RTSP|RTMP|HLS推流(3-3)

    前言:我最近在用ffmpeg研究各种网络推流,小有成果,所以写了一篇推流合集的文章记录一下最近关于推流的研究的进展情况. 我在之前搭建了RTMP和RTSP服务器的基础上(参考<[音视频]RTSP ...

  3. 海康、大华视频监控在浏览器端无插件低延时播放解决方案

    海康.大华视频监控无插件低延时播放解决方案 第一章 应用简介 第二章 方案的实现方式 2.1 方案的技术架构 2.2 功能模块构成 第三章 平台的安装和部署 3.1 视频转码工作站的搭建 3.2 流媒 ...

  4. java视频直播_java视频直播、聊天室、弹幕、多端适配

    [实例简介] JAVA直播后台源码 [实例截图] [核心代码] package com.hushangjie.service; import org.springframework.context.A ...

  5. 记录一些视频直播测试地址 rtmp rtsp http

    作者地址 https://blog.csdn.net/m0_37677536/article/details/83304674

  6. 视频直播 > 最佳实践 > 如何降低延时

    如何降低延时? 更新时间:2020-09-17 18:51:14 本页目录 GOP 帧设置 服务器缓存设置 确认使用的播放协议 按正常情况,RTMP 推流 + FLV 播放的正常延迟在 2-3s 左右 ...

  7. Android 直播 播放器 IJK播放器低延时120ms

    基于上一篇文件rtmp推流<推流文章地址传送门>,需要解码播放器 ,由于是直播 所以需要延时优化到极致,采用播放器也是ijk开源播放器直接修改,目前测试1080p+音频延时效果如下图: 在 ...

  8. 【云中沙箱】视频直播技术浅析与实践!

    作者:鲍天舒  公司:上海驻云信息科技有限公司 云中沙箱,阿里云官方实验平台.网址:http://lab.aliyunedu.net 云中沙箱实验,教您如何部署视频直播平台! 1. "快速部 ...

  9. 使用Java实现视频直播解决方案

    使用Java实现视频直播解决方案 1.概述 本博客使用JavaCV开发的rtsp流转rtmp流并进行推流,并使用nginx实现流媒体直播方案 1.1 网络摄像头协议(一般网络摄像头支持协议有GB/T2 ...

最新文章

  1. 判断人工智能是否可信的“四把尺子”
  2. Navicat for Oracle工具连接oracle
  3. linux shell 单行多行注释
  4. win7下查找端口,使用netstat命令注意事项
  5. 安卓2.3刷机包_红米K20Pro 安卓Q刷机包下载+刷机教程
  6. java实现颜色Color对象和16进制之间的转换
  7. .NEt中的继承、聚合和组合
  8. 编译程序和解释程序有哪些区别?
  9. 8004.ros2中添加boost依赖库写法
  10. 李开复对谈硅谷传奇:杨致远敦促AI交产品,马尔科夫说无人车3年没戏
  11. JavaOO 常用类新增
  12. 【Unity游戏开发基础】如何做可以调整音量的UI滚动条组件
  13. layui date插件设置不能跨月查询
  14. project sms / BSS / OSS / ESS / dianxin / youbian / iccid / puk / pin
  15. Windows下批处理一键修改系统时间并运行程序
  16. 516. Longest Palindromic Subsequence
  17. 阅读笔记04——魔鬼搭讪学
  18. THREEJS辉光与景深特效
  19. 安卓小人html制作,告白小人在线制作
  20. .bin文件 .hex文件和.s19文件区别

热门文章

  1. 计算机网络自考真题,自考计算机网络历年真题及答案汇编.pdf
  2. 【BP分类】基于哈里斯鹰算法优化BP神经网络实现数据分类含Matlab源码
  3. 经典SQL快速学习教程
  4. Windows关机/重启命令
  5. 前端笔记-——空格、」、「符号的使用
  6. 数学物理方程的Matlab实现
  7. 为什么要采用全网营销策略?全网营销有何优势?
  8. python交流企鹅裙_动漫迷的福利!Python小白也可以学会的爬虫教程
  9. 网络安全—如何从IP源地址角度,预防DDoS攻击?
  10. app能不能跳转外部h5_APP内部H5页面跳转 H5唤起APP 怎么做?