使用FFmpeg + nginx + flv.js 实现RTSP格式视频流在web网页进行播放_rtmp://127.0.0.1:1935/liveapp/test: i/o error-程序员宅基地

技术标签: nginx  http-flv  vue  RTSP  video  ffmpeg  

近期,出于项目需要,要求支持web网页播放RTSP格式的监控视频。因之前并没有相关的项目经验及技术积累。并且H5中的video默认并不支持RTSP格式的视频播放,接下来花了一周时间,都在进行调研和实践网上搜到的方案。

1、vlc插件 + video标签

起初,找到的实现方式,是利用第三方插件,vlc播放器或者在谷歌浏览器上安装vlc视频插件的方案。在实践过程中发现,该方案,依赖于谷歌浏览器支持NPAPI插件。谷歌浏览器目前已经不知道该插件,即使后来费了很多时间在网上找谷歌浏览器40-44版本,尝试进行播放,页面也提示“该插件不支持”。同时,低版本谷歌浏览器基本是2015年之前的,对于ES6语法还有样式上的兼容,也多少存在一些问题,还有就是下载的版本居然连打开开发者工具都闪退,果断放弃了在低版本谷歌浏览器的开发计划。

之后有尝试谷歌浏览器能不能播放RTMP格式的视频流,结果依旧没有成功;究其原因,是由于2021年之后的谷歌浏览器版本已不再支持flash插件,且之前的任何浏览器版本都不再支持flash插件,即使在设置中打开的flash的控制,页面依然显示“adobe flash player 不再支持”的字样。。。

2、nginx + ffmpeg + flv.js

翻找了很多相关的技术文章,esayPlayer也安装过,加开发群咨询技术问题,反馈都说前端H5无法直接播放RTSP视频流,只能转格式,但是推荐的服务又是付费的,目前项目并不打算使用第三方服务,将视频放在公网环境进行存储播放,所以也就作罢了。

最终,选择了这个看上去技术实践并不是很困难的方式。该方式的实现原理,通过服务端将其 RTSP / RTMP 流实时转为 http-flv 流,从而浏览器可直接使用该流进行直播(使用bilibili提供的 flv.js )。

该实现方式的前提条件:
(1)nginx

(2)nginx-http-flv-module

(3)ffmpeg

  (4)  flv.js

2.1 配置流程

2.1.1 nginx 补充 nginx-http-flv-module模块

// 步骤:

// 1、下载 nginx-http-flv-module

下载 https://github.com/winshining/nginx-http-flv-module 项目


// 2、下载 nginx 安装包,解压后进入对应包文件夹中

cd /usr/local

tar -zxvf nginx-1.8.1.tar.gz

cd nginx-1.21.1 


// 3、在nginx安装文件夹中,执行以下命令,安装nginx-http-flv-module,--add-module后拼接的是nginx-http-flv-module 项目的存放路径

./configure --prefix=/usr/local/nginx  --add-module=/usr/local/nginx/nginx-http-flv-module


// 4、手动编译并重新安装nginx,此时nginx将被安装到 

make && make install


// 5、重启nginx

nginx -s reload 

遇到的问题:

(1)已有nginx,安装路径下,并没有 configure文件

我是在mac上直接进行配置操作的,但是进入nginx的安装路径 /usr/local/Cellar/nginx下(之前通过brew install nginx 进行安装的),并没有找到 congfigure文件。在网上找了nginx的安装压缩包,拷贝其中的 congfigure 进行了补充。但由于是第一次搞这个配置,怕又出什么幺蛾子,就先用 brew uninstall nginx 把之前的卸载了。重新按照上边的方式,一步步进行了操作;

(2)nginx补充模块时报openssl库缺失

./configure: error: SSL modules require the OpenSSL library.
You can either do not enable the modules, or install the OpenSSL library
into the system, or build the OpenSSL library statically from the source
with nginx by using --with-openssl=<path> option.


依照该报文提示,先检查是否已经安装openssl库

如果没有, 执行安装命令  brew  install openssl

然后,修改 nginx 安装第三方模块的命令, --with-openssl=拼接openssl的安装路径:

./configure --prefix=/usr/local/nginx  --add-module=/usr/local/nginx/nginx-http-flv-module --with-openssl=/usr/local/Cellar/[email protected]/1.1.1i

出现以下类似文案,表明命令执行成功:

Configuration summary
  + using system PCRE library
  + using OpenSSL library: /usr/local/Cellar/[email protected]/1.1.1i
  + using system zlib library

  nginx path prefix: "/usr/local/nginx"
  nginx binary file: "/usr/local/nginx/sbin/nginx"
  nginx modules path: "/usr/local/nginx/modules"
  nginx configuration prefix: "/usr/local/nginx/conf"
  nginx configuration file: "/usr/local/nginx/conf/nginx.conf"
  nginx pid file: "/usr/local/nginx/logs/nginx.pid"
  nginx error log file: "/usr/local/nginx/logs/error.log"
  nginx http access log file: "/usr/local/nginx/logs/access.log"
  nginx http client request body temporary files: "client_body_temp"
  nginx http proxy temporary files: "proxy_temp"
  nginx http fastcgi temporary files: "fastcgi_temp"
  nginx http uwsgi temporary files: "uwsgi_temp"
  nginx http scgi temporary files: "scgi_temp"

 

 (3)执行make install 提示没有权限

// 错误报文

/Library/Developer/CommandLineTools/usr/bin/make -f objs/Makefile install
test -d '/usr/local/nginx' || mkdir -p '/usr/local/nginx'
mkdir: /usr/local/nginx: Permission denied
make[1]: *** [install] Error 1
make: *** [install] Error 2

处理方案:

sudo make install  // 需要输入密码

  (4)nginx 不存在

// 错误报文
app@xxx local% nginx -v
zsh: command not found: nginx

处理方案:

修改/etc/profile文件

进入文件编辑 vim /etc/profile 

输入 i 进入编辑状态

# System-wide .profile for sh(1)

if [ -x /usr/libexec/path_helper ]; then
	eval `/usr/libexec/path_helper -s`
fi

if [ "${BASH-no}" != "no" ]; then
	[ -r /etc/bashrc ] && . /etc/bashrc
fi

// 在该文件末尾补充以下文字,注意nginx安装后的路径

PATH=$PATH:/usr/local/nginx/sbin
export PATH

按 esc 退出编辑状态
按 :wq 强制更新文件
cat  /etc/profile 查看编辑后的文件内容

最后,记得执行   source /etc/profile  重启该配置文件

输入以下结果表明全局可使用nginx

app@xxx local% nginx -v
nginx version: nginx/1.21.1

2.1.2 修改 nginx 配置文件

该配置,按照nginx-http-flv-module 项目中提供的配置进行修改即可,以下是我的nginx.conf中相关的配置——标记“重要”的配置。

配置修改完成之后,记得重启nginx服务: nginx -s reload

#user  nobody;
worker_processes  1;

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

#pid        logs/nginx.pid;


events {
    worker_connections  1024;
}


http {
    include       mime.types;
    default_type  application/octet-stream;

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

    #access_log  logs/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    #keepalive_timeout  0;
    keepalive_timeout  65;

    #gzip  on;

    server {
        listen       80;
        server_name  localhost;

        #charset koi8-r;

        #access_log  logs/host.access.log  main;

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



        # 重要 http-flv  转流配置

        location /live {
            flv_live on; #打开 HTTP 播放 FLV 直播流功能
            chunked_transfer_encoding on; #支持 'Transfer-Encoding: chunked' 方式回复

            add_header 'Access-Control-Allow-Origin' '*'; #添加额外的 HTTP 头
            add_header 'Access-Control-Allow-Credentials' 'true'; #添加额外的 HTTP 头
            add_header 'Cache-Control' 'no-cache';
        }





        #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;
        }
    }

}

# 重要  rtmp推流相关配置

rtmp_auto_push on;
rtmp_auto_push_reconnect 1s;
rtmp_socket_dir /tmp;
 
rtmp {
    out_queue           4096;
    out_cork            8;
    max_streams         128;
    timeout             15s;
    drop_idle_publisher 15s;
 
    log_interval 5s; #log模块在access.log中记录日志的间隔时间,对调试非常有用
    log_size     1m; #log模块用来记录日志的缓冲区大小
 
    server {
        listen 1935;
        # server_name www.test.*;  #当模块中,只有一个server时,可以不配置server_name,nginx对于请求,当找不到匹配的server_name时,会默认发给第一个server进行处理。
 
        application myapp {
            live on;
            gop_cache on; #打开GOP缓存,减少首屏等待时间
        }

    }
}

2.2 执行拉流、推流操作

// ffmpeg的拉流、推流命令,可自行搜索,以下命令,是将一个本地RTSP的视频流,转为http-flv的过程,
其中视频、音频格式直接复制,没有进行转码操作

ffmpeg -rtsp_transport tcp -i  rtsp://admin:[email protected]:554/Streaming/Channels/101 -c copy -f flv rtmp://127.0.0.1:1935/live/101

遇到的问题

(1)无法执行以上命令,报错: RTMP_ReadPacket, failed to read RTMP packet header

// 错误报文
[rtsp @ 0x7f8429008200] Missing PPS in sprop-parameter-sets, ignoring
Input #0, rtsp, from 'rtsp://admin:[email protected]:554/Streaming/Channels/101':
  Metadata:
    title           : Media Presentation
  Duration: N/A, start: 0.240000, bitrate: N/A
    Stream #0:0: Video: h264 (Main), yuvj420p(pc, bt709, progressive), 1280x720, 25 fps, 25 tbr, 90k tbn, 50 tbc
RTMP_ReadPacket, failed to read RTMP packet header
rtmp://127.0.0.1:1935/live/101: Unknown error occurred

处理方案:

以上报文,表明拉流操作正常,推流出现了问题,
排查了很久,最后,对比了之前的nginx配置发现

rtmp://127.0.0.1:1935/live/101  中的 live 没有对应配置,将其修改为 myapp 后成功执行


注意: 推流的地址格式———— rtmp://ip:port/appname/streamname  

其中,port  指nginx配置中rtmp相关配置下的监听端口 1935(可自行修改nginx中对应端口,注意不要与其他使用端口冲突)
appname  指 application 后的名称,可配置多个application,注意名称不可重复

nginx配置示例如下:

rtmp {
    out_queue           4096;
    out_cork            8;
    max_streams         128;
    timeout             15s;
    drop_idle_publisher 15s;
 
    log_interval 5s; #log模块在access.log中记录日志的间隔时间,对调试非常有用
    log_size     1m; #log模块用来记录日志的缓冲区大小
 
    server {
        listen 1935;
        # server_name www.test.*;  #当模块中,只有一个server时,可以不配置server_name,nginx对于请求,当找不到匹配的server_name时,会默认发给第一个server进行处理。
 
        application myapp {
            live on;
            gop_cache on; #打开GOP缓存,减少首屏等待时间
        }

    }
}

(2)无法执行以上命令,报错:RTMP_Connect0, failed to connect socket. 61 (Connection refused)

// 错误报文

[rtsp @ 0x7fd41d011000] Missing PPS in sprop-parameter-sets, ignoring
Input #0, rtsp, from 'rtsp://admin:[email protected]:554/Streaming/Channels/101':
  Metadata:
    title           : Media Presentation
  Duration: N/A, start: 0.240000, bitrate: N/A
    Stream #0:0: Video: h264 (Main), yuvj420p(pc, bt709, progressive), 1280x720, 25 fps, 25 tbr, 90k tbn, 50 tbc
RTMP_Connect0, failed to connect socket. 61 (Connection refused)
rtmp://127.0.0.1:1935/myapp/test: Unknown error occurred


处理方案:

网上搜索,提示是nginx服务未开启导致的,重启nginx即可

(3)延迟较大,超过10s以上

       通过以下操作,延迟明显缩小,但是在使用浏览器播放视频源地址,与页面上进行播放的视频进行对比,发现页面上的时间与播放器上的时间,依旧存在3-5s的差距,这个问题待后续跟进处理;

// 修改ffmpeg的命名,
// 补充 -tune zerolatency 控制延迟
// 补充 -preset ultrafast 转码速度


ffmpeg -rtsp_transport tcp -i "rtsp://admin:[email protected]:554/Streaming/Channels/102" -vcodec copy -acodec copy -f flv -tune zerolatency  -preset ultrafast "rtmp://127.0.0.1:1935/myapp/test" 

2.3 前端页面的输出

2.3.1 flv.js 安装

npm i flv.js

2.3.1 vue 页面导入flv.js

播放器组件封装,基本上都是按照flv.js 提供的demo进行配置的

<template>
  <div class="app-container">
    <div>原生video</div>
    <video id="videoElement" class="centeredVideo" controls autoplay width="480" height="320" muted>
      Your browser is too old which doesn't support HTML5 video.
    </video>
  </div>
</template>

<script>
import flvjs from 'flv.js'
export default {

  data() {
    return {
      
    }
  },
  mounted() {
    this.$nextTick(() => {
      if (flvjs.isSupported()) {
        const videoElement = document.getElementById('videoElement')
        var flvPlayer = flvjs.createPlayer(
          {
            type: 'flv',
            url: 'http://127.0.0.1/live?port=1935&app=myapp&stream=test',
            isLive: true, // <====加个这个
            hasAudio: false,
            hasVideo: true
            // withCredentials: false,
            // cors: true
          },
          {
            enableWorker: true, // 开启多线程
            enableStashBuffer: false,
            lazyLoad: false,
            lazyLoadMaxDuration: 0,
            lazyLoadRecoverDuration: 0,
            deferLoadAfterSourceOpen: false,
            fixAudioTimestampGap: true,
            autoCleanupSourceBuffer: true
          }
        )
        flvPlayer.attachMediaElement(videoElement)
        flvPlayer.load() // 加载
        videoElement.play()
      }
    })
  },
  methods: {
   
  }
}
</script>

遗留问题

(1)前端页面支持多个视频同时播放的处理,目前chrome等现代浏览器的同源策略限制,最多播放6个直播视频流;

(2)延迟5s以上的问题,同一个视频源,页面播放与使用vlc播放器直接播放RTSP视频流之间存在3-5s的延迟问题,有待后续跟进处理;

参考文档:

1、web实现RTSP无插件低延迟播放方案整理

2、基于nginx-rtmp-module模块实现的HTTP-FLV直播模块nginx-http-flv-module(一)

3、直播流转码 RTMP 转 HTTP-FLV 用于 WEB 播放解决流程

4、【入门】无插件web直播解决方案,ffmpeg+nginx-http-flv-module+flv.js

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/rushichunqiu/article/details/118858076

智能推荐

MongoDb-GridFs-分片_mongodb对gridfs进行分片-程序员宅基地

文章浏览阅读357次。前置要求MongoDb需要使用Shard方式部署 需要安装好 mongod、mongos、replica set使用GridFs存储文件GridFs存储文件的方式GridFs使用两个集合存储文件:files、chunks。如果不做指定,默认会使用前缀【fs.】files其中files是文件元数据,存储的是文件的一些信息。不建议分片,存储的数据就是一个简单的doc,数据量较小,如果需要使用分片,建议用_id分片。{ "_id" : 文件ObjectId, 【默认索引字段】_mongodb对gridfs进行分片

小旋风百度SEO批量PING推送工具_小旋风ping工具-程序员宅基地

文章浏览阅读918次。介绍独创模拟用户推送(无需切换IP),吸引蜘蛛快速收录大杀器!电脑需安装 .NET Framework 4.0 以上,自行检查!1、批量导入网址(一行一条url地址)2、万级数量URL秒级导入3、多线程发包,日推送量百万以上4、独创模拟真实用户发包推送,无需切换IP百度PING推送不用token 和 Cookie 一天无限推送链接提交给百度收录,这款工具非常好用,亲测一天收录增加20000多!这款工具原版绿色需要2500 这款无需绿色免费使用。现在便宜出售。一天可推送几百万条连接给bai_小旋风ping工具

scala函数式编程_想要开始进行函数式编程,请进入scala-程序员宅基地

文章浏览阅读283次。scala函数式编程 意见 (Opinion)If you haven’t used Scala yet, you’re not the only one: Not even four percent of all programmers were using the language as of last year, according to 如果您还没有使用Scala,那么您不是唯一的一个...

Android开发笔记之自定义控件(物流时间轴的实现)_android 类似快递时间轴控件-程序员宅基地

文章浏览阅读3.9k次。最近修改项目遇到查看物流这个需求,经过一个下午的时间的终于搞定,趁着这个时间点,赶快把这个功能抽取出来,方便大家以后开发的需要,帮助到更多的人 先看效果图,如下 看完之后,分析可知道,主要是两部分,一个头部和一个body. 那我们最主要的工作就是body内容的实现,头部的实现简单,这里就不再详细的说明 这里我给大家提供一个github上的开源项目,不过这个实现起来,绘制的效果比较慢,不过_android 类似快递时间轴控件

element-ui+vue给登录界面创建一个走马灯幻灯片切换_elementui走马灯图片-程序员宅基地

文章浏览阅读3.3k次,点赞8次,收藏24次。效果想要达到的效果如下图,顶部横幅有6张图片可以自动切换:实现步骤先去element-ui官网学习这个跑马灯的模板代码:<template> <el-carousel :interval="4000" type="card" height="200px"> <el-carousel-item v-for="item in 6" :key="item"> <h3 class="medium">{{ item }}</h3_elementui走马灯图片

随便推点

hrbust 1739 Sort Problem 模拟_zcmu1739-程序员宅基地

文章浏览阅读347次。Sort ProblemTime Limit: 1000 MSMemory Limit: 65535 KTotal Submit: 343(88 users)Total Accepted: 182(86 users)Rating: Special Judge: YesDescription_zcmu1739

【数据结构】数组与广义表-螺旋矩阵的实现(图解、c++、java)_数据结构螺旋方阵-程序员宅基地

文章浏览阅读1.1k次,点赞2次,收藏4次。URLeisure的螺旋矩阵的实现“完美”复习资料。_数据结构螺旋方阵

UI自动化中,常见JS处理_ui自动化如何向只读属性输入数据-程序员宅基地

文章浏览阅读490次。UI自动化_JS处理1)下拉滚动条from selenium import webdriverimport time driver = webdriver.Chrome()driver.maximize_window()driver.get('https://www.baidu.com/')driver.find_element_by_id('kw').send_keys('自动化测试')driver.find_element_by_id('su').click()time.sleep_ui自动化如何向只读属性输入数据

安装bochs并配置linux0.11-程序员宅基地

文章浏览阅读189次。为什么80%的码农都做不了架构师?>>> ..._bochs rootimage-0.11

BUUCTF:【x_nuca_2018_offbyone2】(off by null)-程序员宅基地

文章浏览阅读1.2k次。在buu里挑了一道heap的题,是一道off by null 的题,比较容易,经典一些例行检查:_x_nuca_2018_offbyone2

推荐文章

热门文章

相关标签