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 {
@Autowired
ThreadPoolTaskExecutor 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;
@Data
public 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>() {
@Override
protected 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>() {
@Override
protected 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>() {
@Override
protected 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 的系统中安装该功能,再使用最新版本
上一篇文章介绍了通用协议onvif获取到rtsp地址Java onvif协议通用协议获取rtsp地址当然也有很多其他的方式获取rtsp地址首先还是引入包:<!--javacv--> <dependency> <groupId>org.bytedeco</groupId> <artifactId>javacv-platform</artifactId> .
javacv以udp组播方式推送桌面视频流,拉取udp流并播放,实现同屏浏览
采用的javacv版本javacv-platform-1.5-bin,将javacv-platform-1.5-bin的所有jar都拷贝到了工程的lib目录,并引用了javacpp.jar, javacv-platform.jar,opencv-windows-x86_64.jar
由于所有的jar有近500兆,没有随源码一起上传,可在网上下载
在一个月之前,有使用过FFmpeg录制过rtsp流的视频。但由于使用的是Frame来录制视频,会极大的消耗CPU和内存(CPU约为200%+,内存约为2.3G)。经研究得知grabber.grabFrame()会经过解码得到Frame,在record(frame)时又会通过编码生成对应的视频文件。
而如果使用AvPacket(转封装)来实现,在转封装的基础上还用到了多线程分别多拉流和推流进行处理。录
java实现超低延迟 webrtc 直播
文章目录java实现超低延迟 webrtc 直播环境描述定义、首字母缩写词和缩略词目标主要流程信令服务器 -- RabbitMQKurentoCoturn 搭建附页NAT种类NAT 穿透原理ICE
通过转化 RTSP/ RTMP 视频流为 webrtc 推送到云端,实现超低延迟的视频直播,涉及到许多计算机网络的知识,这里就不深入讲解,感兴趣的可以自行研究。
JDK 1.8
kurento
RabbitMQ
coturn
海康摄像头
FFmpegWebPusher
基于FFmpeg、SpringBoot、JavaCV开发的远程推流服务,可通过http-api实现远程拉流、推流、转发流等功能,实现拉流直播、第三方直播等功能。
之前使用node实现的简易版,
环境与依赖
IDEA-2018
Build:
Maven
3.6.3
VERSION:
SpringBoot
2.2.2
VERSION:
Oracle
8u201
VERSION:
FFMPEG
4.0.6
VERSION:
GLIBC
通过SpringBoot提供
Restful
使用Java线程池技术,支持并发推流
使用,相比直接调用ffmpeg命令行,更加灵活,支持更多的定制。
容器化部署,简化部署流程
由于同时需要java8以及ffmpeg环境,为了部署方便,通过容器的方式简化部署流程。
镜像地址:
docker
zwboy/ffmpeg-web-pusher
docker
敬告:该系列的课程在抓紧录制更新中,敬请大家关注。敬告: 该系列的课程涉及:FFmpeg,WebRTC,SRS,Nginx,Darwin,Live555,等。包括:音视频、流媒体、直播、Android、视频监控28181、等。我将带领大家一起来学习:学习onvif、gsoap,亲手生成onvif的框架代码(包括windows和Linux两套环境);完成onvif功能的代码封装;具体内容包括: 1.onvif协议讲解,包括原理、规范、协议内容、实现机制、技术理解、测试工具等。2.webservice,soap与gsoap实战,使用gSOAP创建SOAP调用实例。3.Windows10环境onvif框架代码实战,亲手生成框架代码,并完成onvif主要功能的代码封装。4.Linux(ubnutu18)环境onvif框架代码实战,亲手生成框架代码,并完成onvif主要功能的代码封装。
1、本项目为IDEA 2021工具创建的Spring Boot项目。
2、程序使用的中间件有:redis、nginx、rtmp、ffmpeg。
3、运行程序前需安装以上插件并在application.yml中配置。
4、本程序的功能为:
1)、拉取RTSP流转推RTMP,支持H265转H264。
2)、可选择使用javaCV推流、ffmpeg推流两种方式。
3)、javaCV仅支持拉去或推送H264压缩方式的流。
4)、ffmpeg采用命令的方式拉流和推流,支持H265和H264。
5)、支持获取流的访问数,可设置自动关闭0访问数的推流以节约系统资源。
6)、linux系统支持推流前系统资源限制。
7)、完整的日志输出,便于查看项目运行状况。
5、ffmpeg需要下载linux版本程序,并将路径添加至系统环境,像配置java环境一样。
6、本程序仅供学习交流,请勿用于任何商业场景,由于使用本程序造成的任何损失需自行承担。
要实现无控件HTML页面视频实时预览、录像等功能,可以结合Java、FFmpeg和JavaCV来实现。RTSP(Real Time Streaming Protocol)是一种实时传输协议,用于实现音视频的实时传输。
首先,需要使用JavaCV来调用FFmpeg进行视频处理。JavaCV是一个基于Java的开源项目,它提供了访问OpenCV、FFmpeg等多媒体库的接口。
在Java代码中,可以使用JavaCV提供的API,连接RTSP流,获取实时视频流数据。通过FFmpeg,可以解码视频流数据,并将解码后的数据进行处理和渲染。
在HTML页面中,可以使用HTML5的video标签来实现视频播放。将实时接收到的视频数据通过WebSocket等技术传输到前端,然后使用JavaScript将数据解析,并通过video标签动态渲染视频。
同时,可以使用Java代码实现视频录像功能。可以使用JavaCV提供的API,将实时视频数据保存为视频文件,实现录像功能。
总结一下,要实现无控件HTML页面视频实时预览、录像等功能,可以结合Java、FFmpeg和JavaCV来实现。使用JavaCV连接RTSP流,获取实时视频流数据,通过FFmpeg解码视频流数据,并进行处理和渲染。在HTML页面中使用video标签实现视频播放,并通过Java代码实现视频录像功能。