跳至主要內容

使用 ffmpeg 和 Nginx 转发海康摄像头视频流

牧歌...大约 8 分钟

使用 ffmpeg 和 Nginx 转发海康摄像头视频流

背景说明

摄像头网络与服务器网络在2个网段,摄像头网络在0网段局域网没有外网,服务器网络在1网段可以连接外网同时可以访问0网段。

现需要在其他1网段客户端得到这个视频流

image.png|650
image.png|650

服务器环境 CentOS Linux

安装 Nginx 和 nginx-http-flv-module

安装环境依赖(192.168.1.190)

yum install gcc gcc-c++ autoconf automake -y
yum install zlib zlib-devel -y
yum install pcre pcre-devel -y
yum install openssl openssl-devel -y
yum install patch -y
yum install git -y

进入src目录

#进入src目录
cd /usr/local/src

下载 Nginx

这里我又安装了一个正向代理的 module,不用的可以去掉

#下载nginx源码包
wget http://nginx.org/download/nginx-1.18.0.tar.gz
#解压nginx源码包
tar zxvf nginx-1.18.0.tar.gz

下载正向代理第三方模块(这个不用,是我做其他事情的插件)

#克隆第三方模块项目到本地
git clone https://github.com/chobits/ngx_http_proxy_connect_module.git

#进入解压后的nginx目录
cd nginx-1.18.0
#根据官方文档对第三方模块进行处理(patch一定要在 nginx 解压目录里面执行)配置 正向代理模块
patch -p1 < /usr/local/src/ngx_http_proxy_connect_module/patch/proxy_connect_rewrite_101504.patch

下载 nginx-http-flv-module 第三方模块

image.png|650
image.png|650

官方文档有点坑没说咋下载,一笔带过,哭死。 Github 官方文档open in new window

网上好多git的地址不好用

git clone https://github.com/winshining/nginx-http-flv-module.git

使用 wget 好用

#下载nginx-http-flv-module
wget https://github.com/winshining/nginx-http-flv-module/archive/master.zip
#解压 nginx-http-flv-module
unzip master.zip
 
#一般将这个放在usr/local,目录下。这里放在了 usr/local/src 下了

配置 Nginx

#配置nginx参数
./configure --prefix=/usr/share/nginx --sbin-path=/usr/sbin/nginx --modules-path=/usr/lib64/nginx/modules --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --http-client-body-temp-path=/var/lib/nginx/tmp/client_body --with-http_stub_status_module --http-proxy-temp-path=/var/lib/nginx/tmp/proxy --http-fastcgi-temp-path=/var/lib/nginx/tmp/fastcgi --http-uwsgi-temp-path=/var/lib/nginx/tmp/uwsgi --http-scgi-temp-path=/var/lib/nginx/tmp/scgi --pid-path=/run/nginx.pid --lock-path=/run/lock/subsys/nginx --user=nginx --group=nginx --with-file-aio --with-http_ssl_module --with-http_v2_module --with-http_realip_module --with-stream_ssl_preread_module --with-http_addition_module --with-http_gunzip_module --with-http_gzip_static_module   --with-stream_ssl_module  --add-module=/usr/local/src/ngx_http_proxy_connect_module
#编译
make
#编译后的安装
make  install

验证插件是否安装

#查看是否已经添加了rtmp模块
nginx -V

#出现configure arguments: --add-module=../nginx-http-flv-module-master
#说明已经添加了rtmp模块

修改配置文件


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

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

	# 正向代理的2个server配置
	server {
    		resolver 114.114.114.114;
    		listen 3128;
    		location / {
        		proxy_pass http://$http_host$request_uri;
        		proxy_set_header HOST $http_host;
        		proxy_buffers 256 4k;
        		proxy_max_temp_file_size 0k;
        		proxy_connect_timeout 30;
        		proxy_send_timeout 60;
        		proxy_read_timeout 60;
        		proxy_next_upstream error timeout invalid_header http_502;
    		}
	}
	server {
     		listen                         8443;
     		resolver                       114.114.114.114;
     		proxy_connect;
     		proxy_connect_allow            443 563;
     		proxy_connect_connect_timeout  10s;
     		proxy_connect_read_timeout     10s;
     		proxy_connect_send_timeout     10s;

     		location / {
         		proxy_pass http://$host;
         		proxy_set_header Host $host;
     		}
	}

	# 视频流代理
	server {
		# 监听的接口使用8991,自定义
        listen       8991;

        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 头
        }
    }
}

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.*; #用于虚拟主机名后缀通配

        application myapp {
            live on;
            gop_cache on; #打开 GOP 缓存,减少首屏等待时间
        }

        application hls {
            live on;
            hls on;
            hls_path /tmp/hls;
        }

        application dash {
            live on;
            dash on;
            dash_path /tmp/dash;
        }
    }

    server {
        listen 1935;
        server_name *.test.com; #用于虚拟主机名前缀通配

        application myapp {
            live on;
            gop_cache on; #打开 GOP 缓存,减少首屏等待时间
        }
    }

    server {
        listen 1935;
        server_name www.test.com; #用于虚拟主机名完全匹配

        application myapp {
            live on;
            gop_cache on; #打开 GOP 缓存,减少首屏等待时间
        }
    }
}

启动Nginx

#测试配置文件是否正确,不正确去看日志
nginx -t
#启动
nginx
#停止
nginx -s stop
#重载配置文件
nginx -s reload

验证插件是否生效

正向代理,通过查看listen端口是否启动

netstat -nutpl
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name    
tcp        0      0 0.0.0.0:3128            0.0.0.0:*               LISTEN      7443/nginx: master  
tcp        0      0 0.0.0.0:80              0.0.0.0:*               LISTEN      7443/nginx: master  
tcp        0      0 0.0.0.0:8443            0.0.0.0:*               LISTEN      7443/nginx: master

正向代理文章参考:Nginx正向代理open in new window

nginx-http-flv-module

hint:可以通过linux查看端口占用情况

lsof -i:1935

如果1935端口开启,会有类似如下的信息:

[root@k8s-node1 nginx]# lsof -i:1935
COMMAND  PID USER   FD   TYPE    DEVICE SIZE/OFF NODE NAME
nginx   3387 root    7u  IPv4 247733434      0t0  TCP *:macromedia-fcs (LISTEN)
nginx   3388  www    7u  IPv4 247733434      0t0  TCP *:macromedia-fcs (LISTEN)

ffmpeg 安装

安装yasm

yum install yasm

安装 ffmpeg

wget http://www.ffmpeg.org/releases/ffmpeg-4.1.tar.gz
tar -zxvf ffmpeg-4.1.tar.gz
cd ffmpeg-4.1/
./configure --prefix=/usr/local/ffmpeg
# 这一步很慢,大概5-10分钟,耐心等待
make && make install

# 去试试
cd /usr/local/ffmpeg/bin/
./ffmpeg -version

# 查看当前bin路径,给环境变量路径使用
pwd
vim /etc/profile
# 在最后PATH添加环境变量
export PATH=$PATH:/usr/local/ffmpeg/bin
# 保存退出 更新
source /etc/profile

# 找一个其他文件测试 ffmpeg 环境变量是否生效
ffmpeg -version

# 开始使用
ffmpeg -rtsp_transport tcp -i "rtsp://账户:密码@IP:554/h265/ch1/main/av_stream" -c copy -f flv rtmp://192.168.1.190:1935/myapp/stream1

ffmpeg官方文档open in new window

播放指令:

ffplay rtsp://admin:hj123456@192.168.1.64:554/h264/ch1/main/av_stream

成功后编写shell脚本启动接入多个摄像头,进入路径/root/app/transmit_camera.sh

# 转发过磅车牌号摄像头

nohup ffmpeg -rtsp_transport tcp -i "rtsp://账户:密码@IP1:554/h265/ch1/main/av_stream" -c copy -f flv rtmp://192.168.1.190:1935/myapp/stream16 > r_camera_16.log 2>&1 &

nohup ffmpeg -rtsp_transport tcp -i "rtsp://账户:密码@IP2:554/h265/ch1/main/av_stream" -c copy -f flv rtmp://192.168.1.190:1935/myapp/stream17 > r_camera_17.log 2>&1 &

RTSP流访问海康摄像头

参数作用
username用户名
passwd密码
ip摄像头ip
port摄像头端口
codec视频编码:h264、h265
channel通道号:ch1、ch2
subtype码流:主码流[main]、子码流[sub]、第三码流 、
codec
#rtsp://[username]:[passwd]@[ip]:[port]/[codec]/[channel]/[subtype]/av_stream
rtsp://username:passwd@0.0.0.0:554/h265/ch2/main/av_stream

以下是一个典型的RTSP数据URL格式: rtsp://username:password@camera_ip:port/h264/ch1/main/av_stream

其中,usernamepassword是摄像头的登录信息,camera_ip是摄像头的IP地址,port是RTSP服务的端口号(默认为554),h264表示视频编码格式,ch1表示通道号,main表示主码流类型。

例如,假设摄像头的IP地址为192.168.1.64,用户名为admin,密码为a12345678,那么RTSP数据URL可以表示为: rtsp://admin:a12345678@192.168.1.64:554/h264/ch1/main/av_stream

VUE播放 flv 格式视频流

Vue 中使用flv.js视频播放器,并将其组件化 https://www.jianshu.com/p/894b77cf7433

优化:延迟卡顿优化 https://www.cnblogs.com/xiahj/p/flvExtend.html

定时脚本自动启动

运行过程中防止程序异常停止或者服务器重启,写一个脚本自动启动。运行几天发现晚上时程序不会停止但推流卡住了,监听CPU占用率10次如果都为0,杀死进程重启一下程序,脚本如下:

#! /bin/bash
. /etc/profile
. ~/.bash_profile

# 定义函数,传入一个PID作为参数
function check_and_kill_pid {
    echo "call function...$1" >> /root/app/task.txt
    local pid=$1
    local threshold=10  # 连续为零的次数阈值
    local count=0       # 计数器

    # 检查是否传入了PID参数
    if [ -z "$pid" ]; then
        echo "没有传入PID参数" >> /root/app/task.txt
        return 1
    fi

    echo "开始监控 PID $pid 的CPU占用..." >> /root/app/task.txt

    # 循环检查10次
    for (( i=1; i<=10; i++ ))
    do
        # 获取PID的CPU占用情况
        cpu_usage=$(top -b -n 1 -p $pid | tail -n +8 | awk '{print $9}')
        cpu_usage=${cpu_usage%.*}  # 取整数部分

        # 判断CPU占用是否为零
        if [ "$cpu_usage" -eq 0 ]; then
            count=$((count + 1))
        else
            count=0
        fi

        # 如果连续达到阈值次数,则杀死进程
        if [ $count -ge $threshold ]; then
            echo "PID $pid 的CPU占用连续 $threshold 次为零,将被杀死" >> /root/app/task.txt
            kill $pid
            return 1
        fi

        sleep 1  # 每秒检查一次
    done

    echo "监控结束,PID $pid 的CPU占用未连续 $threshold 次为零" >> /root/app/task.txt
    return 0
}


PROC_NAME=ffmpeg
ProcNumber=`ps -ef |grep -w $PROC_NAME|grep -v grep|wc -l`
if [ $ProcNumber -le 0 ];then
   echo "testPro is not run" >> /root/app/task.txt
   nohup ffmpeg -rtsp_transport tcp -i "rtsp://账户:密码@IP1:554/h265/ch1/main/av_stream" -c copy -f flv rtmp://192.168.1.190:1935/myapp/stream16 > r_camera_16.log 2>&1 &
   nohup ffmpeg -rtsp_transport tcp -i "rtsp://账户:密码@IP1:554/h265/ch1/main/av_stream" -c copy -f flv rtmp://192.168.1.190:1935/myapp/stream17 > r_camera_17.log 2>&1 &
else
   # 检查 pgrep 命令是否可执行
   if ! command -v pgrep > /dev/null; then
     echo "Error: pgrep 命令不可用,请安装或确保具有执行权限。" >> /root/app/task.txt
     exit 1
   fi
   echo "testPro is  running.." >> /root/app/task.txt
   pid1=$(pgrep -f "ffmpeg -rtsp_transport tcp -i "rtsp://admin:123456@192.168.0.16:554/h265/ch1/main/av_stream" -c copy -f flv rtmp://192.168.1.190:1935/myapp/stream16")
   echo "找到的PID1是:$pid1" >> /root/app/task.txt
   check_and_kill_pid $pid1
   # 检查函数的返回值,根据返回值判断是否启动某些操作
   if [ $? -eq 1 ]; then
       echo "need启动pid1..." >> /root/app/task.txt
       nohup ffmpeg -rtsp_transport tcp -i "rtsp://账户:密码@IP1:554/h265/ch1/main/av_stream" -c copy -f flv rtmp://192.168.1.190:1935/myapp/stream16 > r_camera_16.log 2>&1 &
   else
       echo "pid1不操作,因为PID正常运行和CPU占用未连续为零" >> /root/app/task.txt
   fi
   pid2=$(pgrep -f "ffmpeg -rtsp_transport tcp -i "rtsp://admin:123456@192.168.0.17:554/h265/ch1/main/av_stream" -c copy -f flv rtmp://192.168.1.190:1935/myapp/stream17")
   echo "找到的PID2是:$pid2" >> /root/app/task.txt
   check_and_kill_pid $pid2
   if [ $? -eq 1 ]; then
       echo "need启动pid2..." >> /root/app/task.txt
       nohup ffmpeg -rtsp_transport tcp -i "rtsp://账户:密码@IP1:554/h265/ch1/main/av_stream" -c copy -f flv rtmp://192.168.1.190:1935/myapp/stream17 > r_camera_17.log 2>&1 &
   else
       echo "pid2不操作,因为PID正常运行和CPU占用未连续为零" >> /root/app/task.txt
   fi
fi

注意变量名pid1、pid2后等号两边不能有空格

参考文章

摄像头常用地址参考open in new window

# 海康威视摄像头:开启onvif协议open in new window

用这个文章试了试win好用:有一个win启动的脚本Windows上使用FFmpeg实现本地视频推送模拟海康协议rtsp视频流open in new window# Nginx搭建RTMP服务器+FFmpeg实现海康威视摄像头预览open in new window

看似有用,说解决h265 # Nginx+FFmpeg 海康、大华NVR实现rtsp转flv实时预览+录像回放open in new window

文章提到使用VLC查看引流测试 # Windows上搭建Nginx RTMP服务器并使用FFmpeg实现本地视频推流open in new window

让我成功下载 nginx-http-flv-module ,其余的都不好用 # nginx+nginx-http-flv-module在Linux服务器搭建open in new window

让我知道Nginx成功启动 nginx-http-flv-module 插件 【入门】ffmpeg、nginx、nginx-http-flv-module转发rtsp流、VLC查看open in new window

解决海康摄像头RTSP流访问地址含义以及如何拼接 # RTSP流访问海康摄像头open in new window

将本地电脑摄像头推流到Nginx # 利用ffmpeg实现rtmp推流open in new window

上次编辑于:
贡献者: Owen
评论
  • 按正序
  • 按倒序
  • 按热度