org.bytedeco javacv-platform 1.5.4

这里我是使用的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) rawvideo recorder.setFormat("h264"); // 帧数 double frameLength = grabber.getLengthInFrames(); long frameTime = grabber.getLengthInTime(); double v = frameLength * 1000 * 1000 / frameTime; recorder.setFrameRate(v); //百度翻译的比特率,默认400000 recorder.setVideoBitrate(200000); // recorder.setAudioOption("crf", "23"); // 建议从grabber获取AudioChannels // recorder.setAudioChannels(grabber.getAudioChannels()); // recorder.setInterleaved(true); // yuv420p recorder.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自增1 av_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 {
    @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地址首先还是引入包:&lt;!--javacv--&gt; &lt;dependency&gt; &lt;groupId&gt;org.bytedeco&lt;/groupId&gt; &lt;artifactId&gt;javacv-platform&lt;/artifactId&gt; .
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页面视频实时预览、录像等功能,可以结合JavaFFmpegJavaCV实现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页面视频实时预览、录像等功能,可以结合JavaFFmpegJavaCV实现。使用JavaCV连接RTSP流,获取实时视频流数据,通过FFmpeg解码视频流数据,并进行处理和渲染。在HTML页面中使用video标签实现视频播放,并通过Java代码实现视频录像功能。