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

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

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

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、大功告成

相关推荐

怎样设置EditText内部文字被锁定不可删除和修改

在做项目的时候,我曾经遇到过这样的要求,就是跟百度贴吧客户端上的一样,在回复帖子的时候,在EditText中显示回复人的名字,而且这个名字不可以修改和删除,说白了就是不可操作,只能在后面输入内容。在E...

iOS的布局体系-流式布局MyFlowLayout

iOS布局体系的概览在我的CSDN博客中的几篇文章分别介绍MyLayout布局体系中的视图从一个方向依次排列的线性布局(MyLinearLayout)、视图层叠且停靠于父布局视图某个位置的框架布局(M...

浏览器滚动条hover时变粗、改变颜色

今天应UED的要求对项目的滚动条进行美化,原生的滚动条虽然很实用,但确实不美观。用了一些css美化后::-webkit-scrollbar{height:9px;width:9...

QML控件类型:ComboBox、Control(qml buttongroup)

Control一、描述Control是所有控件通用功能的抽象基类型。它从窗口系统接收输入事件,并在屏幕上绘制自身。二、控件布局控件的implicitWidth和implicitHeight通...

学习CSS布局:简单表格布局代码示例

性能优化-学习CSS布局:简单表格布局代码示例CSS是现代Web设计和开发的必备技能之一。而表格布局是Web页面中常用的布局之一,用于展示数据和信息。在这篇文章中,我们将介绍如何使用CSS创建一个简单...

UE5之UMG基础第1篇:统一网格面板(ue5 新功能)

目标:记录和学习UE5的UMG方法制作UI,使用UniformGridPanel制作效果如下:步骤1.增加前言:UniformGridPanel统一网格面板,就是所有子元素大小和间隔等统一,这种效果...

JS的 DOM 尺寸与位置属性(js设置dom属性)

#头条深一度-深度阅读计划#在JavaScript开发中,操作DOM元素的尺寸和位置是常见的任务,尤其是在实现动画、布局调整或响应式设计时。本文将全面解析JavaScript中与DOM...

SpriteJS:图形库造轮子的那些事儿

从2017年到2020年,我花了大约4年的时间,从零到一,实现了一个可切换WebGL和Canvas2D渲染的,跨平台支持浏览器、SSR、小程序,基于DOM结构和支持响应式的,高...

理解CSS中的百分比单位:相对尺寸的核心规则

在CSS中,百分比(`%`)是一种灵活且强大的相对单位,但其具体行为常让开发者感到困惑。本文将深入解析百分比单位的计算规则,帮助你彻底掌握其背后的逻辑。一、百分比的核心:参考系(包含块)百分比的值始...

36个工作中常用的JavaScript函数片段「值得收藏」

作者:Eno_Yao转发链接:https://segmentfault.com/a/1190000022623676前言如果文章和笔记能带您一丝帮助或者启发,请不要吝啬你的赞和收藏,你的肯定是我前进的...

如何使用css完成视差滚动效果?(css 视距)

视差滚动(ParallaxScrolling)是指多层背景以不同的速度移动,形成立体的运动效果,带来非常出色的视觉体验我们可以把网页解刨成:背景层、内容层、悬浮层使用css形式实现视觉差滚动效果的方...

vant-List 列表(vant select)

引入importVuefrom'vue';import{List}from'vant';Vue.use(List);基础用法List组件通过lo...

Vue3问题:如何使用WangEditor富文本?能自定义才是真的会用!

笔者|大澈大家好,我是大澈!今天的问题,来自于上周末问题留言的朋友嘻嘻哈哈。欢迎大家在周末的问题留言推文中,积极进行问题留言,把这周工作日遇到的问题,分享给大家瞧瞧,或者直接进问答群,一起交流唠...

微信小程序开发极简入门(二):样式,页面,数据

前文:微信小程序开发极简入门(一)样式wxss:/**放在页面的wxss**/.scrollarea{flex:1;overflow-y:hidden;}.idx_view{...

AI+Code驱动的M站首页重构实践:从技术债务到智能化开发

本文分享了阿里巴巴找品M站首页重构项目中AI+Code提效的实践经验。面对M站技术栈陈旧、开发效率低下的挑战,我们通过楼层动态化架构重构和AI智能脚手架,实现了70%首页场景的标准化覆盖+30%的...