百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

Java 监控直播流rtsp协议转rtmp、hls、httpflv协议返回浏览器

zhezhongyun 2025-02-21 16:16 43 浏览

Java 监控直播流rtsp协议转rtmp、hls、httpflv协议返回浏览器

目录

  • 一:了解音视频流协议:
  • 二:方案一 rtsp 转rtmp1、下载nginx + nginx-rtmp-module3、cmd 到nginx根目录启动nginx8、查摄像头的rtsp协议格式10、测试rtmp是否转换成功12、为什么放弃了用rtmp
  • 四:方案三rtsp 转httpflv(采用)1、安装nginx-flv-module4.1 采用java代码去执行ffmepg命令6、前端使用flv.js播放:

需求背景:

在做之前的项目的时候有一个对接摄像头实时播放的需求,由于我们摄像头的购买量不是很多,海康威视不给我们提供流媒体云服务器,所以需要我们自己去 一个去满足我们能在浏览器看到监控画面。项目源代码在以前公司没有拷贝就不能复习,最近又在准备面试,所以写了这个博客来复盘和扩展一下,由于我现在没有Liunx,我就用Windows来演示,生产环境还是要使用Liunx,下面这些操作在Liunx也是一样的流程,大家自行百度。

一:了解音视频流协议:

媒体流协议对比

协议

HttpFlv

RTMP

HLS

Dash

全称

FLASH VIDEO over HTTP

Real Time Message Protocol

HTTP Living Streaming


传输方式

HTTP长连接

TCP长连接

HTTP短连接

HTTP短连接

视频封装格式

FLV

FLV TAG

TS文件

Mp4

3gp

webm

原理

同 RTMP ,使用HTTP协议(80端口)

每个时刻的数据收到后立刻转发

集合一段时间的数据,生成TS切片文件(三片),并更新m3u8索引


延时

1~3秒

1~3秒

5~20秒(依切片情况)

数据分段

连续流

连续流

切片文件

切片文件

Html5播放

可通过HTML5解封包播放

(flv.js)

不支持

可通过HTML5解封包播放

(hls.js)

如果dash文件列表是MP4,

webm文件,可直接播放

其它

需要Flash技术支持,不支持多音频流、多视频流,不便于seek(即拖进度条)

跨平台支持较差,需要Flash技术支持

播放时需要多次请求,对于网络质量要求高


二:方案一 rtsp 转rtmp

1、下载nginx + nginx-rtmp-module

nginx:下载地址:
http://nginx-win.ecsds.eu/download/

nginx-rtmp-module:nginx 的扩展,安装后支持rtmp协议,下载地址:
https://github.com/arut/nginx-rtmp-module

解压nginx-rtmp-module到nginx根目录下,并修改其文件夹名为nginx-rtmp-module(原名为nginx-rtmp-module-master)

2、nginx配置文件

到nginx根目录下的conf目录下复制一份nginx-win.conf 重命名 nginx-win-rtmp.conf

nginx-win-rtmp.conf:

#user  nobody;
# multiple workers works !
worker_processes  2;

#error_log  logs/error.log;
#error_log  logs/error.log  notice;
#error_log  logs/error.log  info;

#pid        logs/nginx.pid;

events {
    worker_connections  8192;
    # max value 32768, nginx recycling connections+registry optimization = 
    #   this.value * 20 = max concurrent connections currently tested with one worker
    #   C1000K should be possible depending there is enough ram/cpu power
    # multi_accept on;
}

rtmp {
    server {
        listen 1935;
        chunk_size 4000;
		
        application live {
             live on;
             # 播放时进行回调,如果HttpRespone statusCode不等于200会断开
			 # on_play http://localhost:8081/auth;
        }
		
		application hls {
		     live on; 
		     # 开启hls切片
             hls on;
             # m3u8地址
			 hls_path html/hls;
			 # 一个切片多少秒
			 hls_fragment 8s;
			 # on_play http://localhost:8081/auth;
			 # on_publish http://localhost:8081/auth;
			 # on_done http://localhost:8081/auth;
        }
    }
}

http {
    #include      /nginx/conf/naxsi_core.rules;
    include       mime.types;
    default_type  application/octet-stream;

    #log_format  main  '$remote_addr:$remote_port - $remote_user [$time_local] "$request" '
    #                  '$status $body_bytes_sent "$http_referer" '
    #                  '"$http_user_agent" "$http_x_forwarded_for"';

    #access_log  logs/access.log  main;

#     # loadbalancing PHP
#     upstream myLoadBalancer {
#         server 127.0.0.1:9001 weight=1 fail_timeout=5;
#         server 127.0.0.1:9002 weight=1 fail_timeout=5;
#         server 127.0.0.1:9003 weight=1 fail_timeout=5;
#         server 127.0.0.1:9004 weight=1 fail_timeout=5;
#         server 127.0.0.1:9005 weight=1 fail_timeout=5;
#         server 127.0.0.1:9006 weight=1 fail_timeout=5;
#         server 127.0.0.1:9007 weight=1 fail_timeout=5;
#         server 127.0.0.1:9008 weight=1 fail_timeout=5;
#         server 127.0.0.1:9009 weight=1 fail_timeout=5;
#         server 127.0.0.1:9010 weight=1 fail_timeout=5;
#         least_conn;
#     }

    sendfile        off;
    #tcp_nopush     on;

    server_names_hash_bucket_size 128;

## Start: Timeouts ##
    client_body_timeout   10;
    client_header_timeout 10;
    keepalive_timeout     30;
    send_timeout          10;
    keepalive_requests    10;
## End: Timeouts ##

    #gzip  on;

    server {
        listen       5080;
        server_name  localhost;


        location /stat {
            rtmp_stat all;
            rtmp_stat_stylesheet stat.xsl;
        }
        location /stat.xsl {
            root nginx-rtmp-module/;
        }
        location /control {
            rtmp_control all;
        }
		
		location /hls {
            # Serve HLS fragments
            types {
                application/vnd.apple.mpegurl m3u8;
                video/mp2t ts;
            }
            expires -1;
            add_header Access-Control-Allow-Origin *;
        }

        #charset koi8-r;
        #access_log  logs/host.access.log  main;

        ## Caching Static Files, put before first location
        #location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ {
        #    expires 14d;
        #    add_header Vary Accept-Encoding;
        #}

# For Naxsi remove the single # line for learn mode, or the ## lines for full WAF mode
        location / {
            #include    /nginx/conf/mysite.rules; # see also http block naxsi include line
            ##SecRulesEnabled;
         ##DeniedUrl "/RequestDenied";
         ##CheckRule "$SQL >= 8" BLOCK;
         ##CheckRule "$RFI >= 8" BLOCK;
         ##CheckRule "$TRAVERSAL >= 4" BLOCK;
         ##CheckRule "$XSS >= 8" BLOCK;
            root   html;
            index  index.html index.htm;
        }

# For Naxsi remove the ## lines for full WAF mode, redirect location block used by naxsi
        ##location /RequestDenied {
        ##    return 412;
        ##}

## Lua examples !
#         location /robots.txt {
#           rewrite_by_lua '
#             if ngx.var.http_host ~= "localhost" then
#               return ngx.exec("/robots_disallow.txt");
#             end
#           ';
#         }

        #error_page  404              /404.html;

        # redirect server error pages to the static page /50x.html
        #
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }

        # proxy the PHP scripts to Apache listening on 127.0.0.1:80
        #
        #location ~ \.php$ {
        #    proxy_pass   http://127.0.0.1;
        #}

        # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
        #
        #location ~ \.php$ {
        #    root           html;
        #    fastcgi_pass   127.0.0.1:9000; # single backend process
        #    fastcgi_pass   myLoadBalancer; # or multiple, see example above
        #    fastcgi_index  index.php;
        #    fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
        #    include        fastcgi_params;
        #}

        # deny access to .htaccess files, if Apache's document root
        # concurs with nginx's one
        #
        #location ~ /\.ht {
        #    deny  all;
        #}
    }

    # another virtual host using mix of IP-, name-, and port-based configuration
    #
    #server {
    #    listen       8000;
    #    listen       somename:8080;
    #    server_name  somename  alias  another.alias;

    #    location / {
    #        root   html;
    #        index  index.html index.htm;
    #    }
    #}

    # HTTPS server
    #
    #server {
    #    listen       443 ssl spdy;
    #    server_name  localhost;

    #    ssl                  on;
    #    ssl_certificate      cert.pem;
    #    ssl_certificate_key  cert.key;
    #    ssl_session_timeout  5m;
    #    ssl_prefer_server_ciphers On;
    #    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    #    ssl_ciphers ECDH+AESGCM:ECDH+AES256:ECDH+AES128:ECDH+3DES:RSA+AESGCM:RSA+AES:RSA+3DES:!aNULL:!eNULL:!MD5:!DSS:!EXP:!ADH:!LOW:!MEDIUM;

    #    location / {
    #        root   html;
    #        index  index.html index.htm;
    #    }
    #}

}

3、cmd 到nginx根目录启动nginx

nginx.exe -c conf\nginx-win-rtmp.conf

测试:浏览器输入
http://localhost:5080/stat,看到

代表安装成功

4、下载ffmpeg安装

ffmpeg:一个处理音视频强大的库,我们需要用它来转协议,下载地址:
https://www.gyan.dev/ffmpeg/builds/ ,这里可以下载essential和full版本,essential就是简版,只包含ffmpeg.exe、ffplay.exe、

ffprobe.exe, 而full版本就包含了动态库和相关头文件,方便我们在开发中调用。

5、配置ffmpeg环境变量

将ffmpeg解压后里面的bin路径复制到Path里面去

6、测试ffmpeg

cmd ffmpeg -version 命令看到代表成功

7、下载VLC播放器

下载地址:
https://www.videolan.org/vlc/

8、查摄像头的rtsp协议格式

我这里截图是海康威视的

现在没有测试的流,我找了个点播的rtsp


rtsp://wowzaec2demo.streamlock.net/vod/mp4:BigBuckBunny_115k.mp4,用这个代替是一样的

9、执行ffmpeg命令

ffmpeg强大,命令也是复杂,我们cmd 执行

ffmpeg -re -rtsp_transport tcp -stimeout 20000000 -i "rtsp://wowzaec2demo.streamlock.net/vod/mp4:BigBuckBunny_115k.mp4" -buffer_size 1024000 -max_delay 500000 -codec:v libx264 -r 25 -rtbufsize 10M -s 1280x720 -map:v 0 -an -f flv rtmp://127.0.0.1:1935/live/test


rtsp://wowzaec2demo.streamlock.net/vod/mp4:BigBuckBunny_115k.mp4,是输入源头


rtmp://127.0.0.1:1935/live/test 是输出地址

如果没有报错的话,到现在rtsp就已经转换好了

ffmpeg命令学习:
https://www.jianshu.com/p/df3216a52e59 、
https://blog.csdn.net/fuhanghang/article/details/123565920

10、测试rtmp是否转换成功

我们打开VLC,媒体->打开网络串流->输入
rtmp://127.0.0.1:1935/live/test
-> 播放

11、测试是否成功

等待几秒钟看到有视频播放就是成功了

12、为什么放弃了用rtmp

rtmp的优点是延迟低,效率高,但是在浏览器需要安装flash才能放,也就老版本的浏览器在用,rtmp可能会在别的地方支持,所以还是把他方式方法贴出来了。

三:方案二 rtsp转hls

1、nginx配置:

在前面已经贴出来了,其中这几个是针对hls的

2、执行ffmepg命令

ffmpeg -i "rtsp://wowzaec2demo.streamlock.net/vod/mp4:BigBuckBunny_115k.mp4" -vcodec libx264 -acodec aac -f flv rtmp://127.0.0.1:1935/hls/test

3、查看nginx根目录 -> hls -> test.m3u8 是否生成

生成了代表一切正常

4、m3u8在网页上播放




    
    前端播放m3u8格式视频
    
    
    
    
    


    


source标签的src属性: http://你的nginx ip:nginx http端口/hls/test.m3u8

rtsp转HLS成功!

5、认识一下m3u8格式

m3u8文件里面存储了一个索引,以文本格式打开是这样的

#EXTM3U
#EXT-X-VERSION:3
#EXT-X-MEDIA-SEQUENCE:56
#EXT-X-TARGETDURATION:13
#EXTINF:10.381,
test-56.ts
#EXTINF:10.422,
test-57.ts
#EXTINF:13.453,
test-58.ts

m3u8文件它不是视频源,源头是ts后缀文件

6、为什么放弃了用HLS

转HLS协议及网页加载过程:

ffmepg收到rtsp的流时候,会等一个切片的时间,一个切片时间到了,切片ts会放到服务器中,同时m3u8文件中加一个索引,对应着新进入的切片。网页在加载m3u8的时候,就是读取m3u8中的的索引去加载ts文件,所以在不断的请求ts,对ts进行解析,不断的和TCP握手,这就是为什么HLS延迟高和对网速的要求高的原因,我们监控肯定是要延迟低的,HLS兼容性好,适合点播。

四:方案三rtsp 转httpflv(采用)

1、安装nginx-flv-module

这个插件需要编译,教程:
https://blog.csdn.net/KayChanGEEK/article/details/105095844

我这里已经编译好了,直接下载启动:

https://gitee.com/isyuesen/nginx-flv-file

2、nginx配置

看我git里面的
https://gitee.com/isyuesen/nginx-flv-file/blob/master/conf/nginx.conf,和默认的config差别主要是添加了这几个

rtmp {  
    server {  
        listen 1935;
        # 流复用的最大块大小
        chunk_size 4000;  
        application liveapp { 
            live on;
            # 推流开始
			on_publish http://localhost:8081/auth;
			# 推流关闭
			on_publish_done http://localhost:8081/auth;
			# 客户端开始播放
			on_play http://localhost:8081/auth;
			# 客户端结束播放
			on_play_done http://localhost:8081/auth;
        }  
    }  
}
location /live {
    flv_live on;
    chunked_transfer_encoding on;
    add_header 'Access-Control-Allow-Credentials' 'true'; #add additional HTTP header
    add_header 'Access-Control-Allow-Origin' '*'; #add additional HTTP header
    add_header Access-Control-Allow-Headers X-Requested-With;
    add_header Access-Control-Allow-Methods GET,POST,OPTIONS;
    add_header 'Cache-Control' 'no-cache';
}

3、做java权限认证

nginx rtmp配置中有配置on_publish钩子接口
http://localhost:8081/auth,这个回调HttpResponse stausCode如果不等于200会拒绝I/O,更多回调钩子看:
https://github.com/arut/nginx-rtmp-module/wiki/Directives#on_connect

@PostMapping("/auth")
    public void getVideo(String token, HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
        if (token.equals("tokenValue")) {
            httpServletResponse.setStatus(200);
        } else {
            // 拒绝服务
            httpServletResponse.setStatus(500);
        }
    }

4、执行ffmepg命令:

ffmpeg -re  -rtsp_transport tcp -i "rtsp://wowzaec2demo.streamlock.net/vod/mp4:BigBuckBunny_115k.mp4" -f flv -vcodec h264 -vprofile baseline -acodec aac -ar 44100 -strict -2 -ac 1 -f flv -s 640*360 -q 10 "rtmp://127.0.0.1:1935/liveapp/test"

4.1 采用java代码去执行ffmepg命令

依赖 javaCV


    org.bytedeco
    javacv-platform
    1.5.2
public class App {
    public static void main( String[] args ) throws IOException, InterruptedException {
        String name = "test";
        // rtsp地址
        String rtspDir = "rtsp://wowzaec2demo.streamlock.net/vod/mp4:BigBuckBunny_115k.mp4";
        // rtmp地址
        String rtmpDir = "rtmp://192.168.0.140:1935/liveapp/" + name + "?token=tokenValue";

        String ffmpeg = Loader.load(org.bytedeco.ffmpeg.ffmpeg.class);
        ProcessBuilder pb = new ProcessBuilder(ffmpeg,
                "-re",
                "-rtsp_transport",
                "tcp",
                "-i",
                rtspDir,
                "-f",
                "flv",
                "-vcodec",
                "h264",
                "-vprofile",
                "baseline",
                "-acodec",
                "aac",
                "-ar",
                "44100",
                "-strict",
                "-2",
                "-ac",
                "1",
                "-f",
                "flv",
                "-s",
                "640*360",
                "-q",
                "10",
                rtmpDir
        );
        pb.inheritIO().start().waitFor();
    }
}

5、测试http-flv链接

如果你跟着我做的,那链接就是
http://127.0.0.1:18080/live?port=1935&app=liveapp&stream=test&token=tokenValue,在VLC播放器中点击媒体 -> 打开网络串流 -> 输入
http://127.0.0.1:18080/live?port=1935&app=liveapp&stream=test&token=tokenValue -> 播放

有视频证明你离成功就差最后一步

6、前端使用flv.js播放:




  
  
  
  播放http-flv






7、大功告成

相关推荐

JPA实体类注解,看这篇就全会了

基本注解@Entity标注于实体类声明语句之前,指出该Java类为实体类,将映射到指定的数据库表。name(可选):实体名称。缺省为实体类的非限定名称。该名称用于引用查询中的实体。不与@Tab...

Dify教程02 - Dify+Deepseek零代码赋能,普通人也能开发AI应用

开始今天的教程之前,先解决昨天遇到的一个问题,docker安装Dify的时候有个报错,进入Dify面板的时候会出现“InternalServerError”的提示,log日志报错:S3_USE_A...

用离散标记重塑人体姿态:VQ-VAE实现关键点组合关系编码

在人体姿态估计领域,传统方法通常将关键点作为基本处理单元,这些关键点在人体骨架结构上代表关节位置(如肘部、膝盖和头部)的空间坐标。现有模型对这些关键点的预测主要采用两种范式:直接通过坐标回归或间接通过...

B 客户端流RPC (clientstream Client Stream)

客户端编写一系列消息并将其发送到服务器,同样使用提供的流。一旦客户端写完消息,它就等待服务器读取消息并返回响应gRPC再次保证了单个RPC调用中的消息排序在客户端流RPC模式中,客户端会发送多个请...

我的模型我做主02——训练自己的大模型:简易入门指南

模型训练往往需要较高的配置,为了满足友友们的好奇心,这里我们不要内存,不要gpu,用最简单的方式,让大家感受一下什么是模型训练。基于你的硬件配置,我们可以设计一个完全在CPU上运行的简易模型训练方案。...

开源项目MessageNest打造个性化消息推送平台多种通知方式

今天介绍一个开源项目,MessageNest-可以打造个性化消息推送平台,整合邮件、钉钉、企业微信等多种通知方式。定制你的消息,让通知方式更灵活多样。开源地址:https://github.c...

使用投机规则API加快页面加载速度

当今的网络用户要求快速导航,从一个页面移动到另一个页面时应尽量减少延迟。投机规则应用程序接口(SpeculationRulesAPI)的出现改变了网络应用程序接口(WebAPI)领域的游戏规则。...

JSONP安全攻防技术

关于JSONPJSONP全称是JSONwithPadding,是基于JSON格式的为解决跨域请求资源而产生的解决方案。它的基本原理是利用HTML的元素标签,远程调用JSON文件来实现数据传递。如果...

大数据Doris(六):编译 Doris遇到的问题

编译Doris遇到的问题一、js_generator.cc:(.text+0xfc3c):undefinedreferenceto`well_known_types_js’查找Doris...

网页内嵌PDF获取的办法

最近女王大人为了通过某认证考试,交了2000RMB,官方居然没有给线下教材资料,直接给的是在线教材,教材是PDF的但是是内嵌在网页内,可惜却没有给具体的PDF地址,无法下载,看到女王大人一点点的截图保...

印度女孩被邻居家客人性骚扰,父亲上门警告,反被围殴致死

微信的规则进行了调整希望大家看完故事多点“在看”,喜欢的话也点个分享和赞这样事儿君的推送才能继续出现在你的订阅列表里才能继续跟大家分享每个开怀大笑或拍案惊奇的好故事啦~话说只要稍微关注新闻的人,应该...

下周重要财经数据日程一览 (1229-0103)

下周焦点全球制造业PMI美国消费者信心指数美国首申失业救济人数值得注意的是,下周一希腊还将举行第三轮总统选举需要谷歌日历同步及部分智能手机(安卓,iPhone)同步日历功能的朋友请点击此链接,数据公布...

PyTorch 深度学习实战(38):注意力机制全面解析

在上一篇文章中,我们探讨了分布式训练实战。本文将深入解析注意力机制的完整发展历程,从最初的Seq2Seq模型到革命性的Transformer架构。我们将使用PyTorch实现2个关键阶段的注意力机制变...

聊聊Spring AI的EmbeddingModel

序本文主要研究一下SpringAI的EmbeddingModelEmbeddingModelspring-ai-core/src/main/java/org/springframework/ai/e...

前端分享-少年了解过iframe么

iframe就像是HTML的「内嵌画布」,允许在页面中加载独立网页,如同在画布上叠加另一幅动态画卷。核心特性包括:独立上下文:每个iframe都拥有独立的DOM/CSS/JS环境(类似浏...