前言

本篇是CentOS 7中搭建基础的Nginx RMPT服务器的续篇,记录了Nignx RTMP使用过程中的一些有用的配置和一些坑。
下面的内容,是以文章CentOS 7中搭建基础的Nginx RMPT服务器中的配置为基准进行的修改。

配置stat

nginx-rtmp-module自身提供了统计的功能。按照Github上官方例子进行配置就行。https://github.com/arut/nginx-rtmp-module
首先,拷贝nginx-rtmp-module源码中的stat.xls到nginx默认主目录/usr/local/nginx/html/中。
再配置rtmp的stat, 在默认server中添加如下配置。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# This URL provides RTMP statistics in XML
location /stat {
rtmp_stat all;
# Use this stylesheet to view XML as web page
# in browser
rtmp_stat_stylesheet stat.xsl;
}
location /stat.xsl {
# XML stylesheet to view RTMP stats.
# Copy stat.xsl wherever you want
# and put the full directory path here
root html;
}

访问http://192.168.168.199/stat,就可以看到如下统计页面。
nginx_rtmp_stat_empty.jpg

有流时的页面如下:
nginx_rtmp_stat_with_streaming.jpg

添加后的配置参见: nginx_with_basic_rtmp_hls_stat.conf

配置转码

直播时,很多情况下需要提供给用户多个分辨率的视频流,以便用户根据实际情况进行选取或客户端自动根据网络情况选择合适分辨率的视频流。
下面介绍如何在nginx-rtmp-module中使用ffmpeg对流进行转码。

分为如下几个步骤:

  1. 新建专门用于播放流的application vod
  2. 将原先的live设置为只接收推流, 再设置使用ffmpeg将源流进行转化,再推到对应的专门播放application vod中。

新建application vod

在rtmp server节点中,新建如下的vod application

1
2
3
4
5
6
7
application vod {
live on; # Allows live input from above
hls on; # Enable HTTP Live Streaming
# Pointing this to an SSD is better as this involves lots of IO
hls_path /usr/local/nginx/hls/;
}

修改application live

application live的修改如下:

  • 注释掉原先的配置中的allow play all;,hls on;hls_path xxx,
  • 添加allow play 127.0.0.1; deny play all;,只允许本地播放。
  • 添加ffmpeg转码和resize
    application live的配置变为:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    application live {
    # 开启直播模式
    live on;
    # 允许从任何源push流
    allow publish all;
    # 允许从任何地方来播放流
    #allow play all;
    # 20秒内没有push,就断开链接。
    drop_idle_publisher 20s;
    # 只允许本地播放流
    allow play 127.0.0.1;
    deny play all;
    # 开启HLS
    #hls on; # Enable HTTP Live Streaming
    # Pointing this to an SSD is better as this involves lots of IO
    # 设置hls存放目录
    #hls_path /usr/local/nginx/hls/;
    exec /usr/bin/ffmpeg -i rtmp://localhost/$app/$name -async 1 -vsync -1
    -c:v libx264 -c:a aac -b:v 256k -b:a 128k -ac 2 -vf "scale=480:trunc(ow/a/2)*2" -tune zerolatency -preset veryfast -crf 23 -f flv rtmp://localhost/vod/$name_low
    -c:v libx264 -c:a aac -b:v 768k -b:a 128k -ac 2 -vf "scale=720:trunc\(ow/a/2\)*2" -tune zerolatency -preset veryfast -crf 23 -f flv rtmp://localhost/vod/$name_mid
    -c:v libx264 -c:a aac -b:v 1024k -b:a 128k -ac 2 -vf "scale=960:trunc(ow/a/2)*2" -tune zerolatency -preset veryfast -crf 23 -f flv rtmp://localhost/vod/$name_high
    -c:v libx264 -c:a aac -b:v 1920k -b:a 128k -ac 2 -vf "scale=1280:trunc(ow/a/2)*2" -tune zerolatency -preset veryfast -crf 23 -f flv rtmp://localhost/vod/$name_hd720
    -c copy -f flv rtmp://localhost/vod/$name_src;
    }

其中ffmpeg命令参考自文章Setting Up Adaptive Streaming with Nginx的配置,分为了low,mid,high,hd720src五种格式。此处可以根据实际要求进行修改。

试验

经过上述配置后,现在有了5种size的视频,和rtmp和hls两种播放方式,共计10种流。

添加转码和resize后的配置参见: nginx_with_resize_rtmp_hls.conf

记录原始流

nginx-rtmp-module提供了record模块,来保存接收到的流。配置参见: https://github.com/arut/nginx-rtmp-module/wiki/Directives#record
如下是最简单的配置,将视频流保存到/tmp/streaming_temp中。
首先创建目录/tmp/streaming_temp.

1
2
# mkdir /tmp/streaming_temp
# chown nobody:root streaming_temp

application live中添加如下配置。

1
2
record all;
record_path /tmp/streaming_temp;

推流过后,接收到的视频就被保存下来了

1
2
3
4
# ll /tmp/streaming_temp/
total 4032
-rw-r--r-- 1 nobody nobody 2111772 Jul 30 23:06 xiaozhupeiqi.flv
#

添加转码和resize后的配置参见: nginx_with_resize_rtmp_hls_record.conf

hls_fragment设置无效问题

问题

根据官方Wiki的说明,参数hls_fragment是用来设置HLS分片的长度的。默认是5秒。
但实际操作中,你可能会发现hls_fragment设置的值没有效果。

原因

Issue 1026中翻到了作者arut的回复。

Fragments are started at keyframes. If keyframes are rare, that’s what you get. Try using the -g option of ffmpeg.

hls_fragment设置依赖于视频源中的关键帧,如果源视频中没有关键帧,那么hls_fragment的设置就无效了。可以在ffmpeg中使用-g来插入关键帧。

解决方法

ffmpeg中-g参数的说明: https://trac.ffmpeg.org/wiki/EncodingForStreamingSites#a-g

-g: Use a 2 second GOP (Group of Pictures), so simply multiply your output frame rate * 2. For example, if your input is -framerate 30, then use -g 60.

-g的使用依赖于原始的帧率,如果想要每2秒就插一个关键帧,输入源的帧率是30,那么此处-g就需要设置为60.

举例,处理帧率是30的流时,在ffmpeg转码时,添加上-g 30。每30帧插入一个关键帧,就可以让hls_fragment设置起效了。

1
2
3
4
5
6
exec /usr/bin/ffmpeg -i rtmp://localhost/$app/$name -async 1 -vsync -1
-c:v libx264 -c:a aac -b:v 256k -b:a 128k -ac 2 -g 30 -vf "scale=480:trunc(ow/a/2)*2" -tune zerolatency -preset veryfast -crf 23 -f flv rtmp://localhost/vod/$name_low
-c:v libx264 -c:a aac -b:v 768k -b:a 128k -ac 2 -g 30 -vf "scale=720:trunc\(ow/a/2\)*2" -tune zerolatency -preset veryfast -crf 23 -f flv rtmp://localhost/vod/$name_mid
-c:v libx264 -c:a aac -b:v 1024k -b:a 128k -ac 2 -g 30 -vf "scale=960:trunc(ow/a/2)*2" -tune zerolatency -preset veryfast -crf 23 -f flv rtmp://localhost/vod/$name_high
-c:v libx264 -c:a aac -b:v 1920k -b:a 128k -ac 2 -g 30 -vf "scale=1280:trunc(ow/a/2)*2" -tune zerolatency -preset veryfast -crf 23 -f flv rtmp://localhost/vod/$name_hd720
-c copy -f flv rtmp://localhost/vod/$name_src;

添加转码和resize后的配置参见: nginx_with_resize_rtmp_hls_record_gop.conf

HLS低延迟设置

nginx-rtmp-modulehls_fragment设置默认是5秒,hls_playlist_length设置默认是30s。
理论上默认配置下,HLS的延迟最大为30s。
设置成如下配置,通过降低ts片段时间,在网络通顺的情况下,实测延迟可以保持在4~5s左右。

1
2
hls_fragment 1s;
hls_playlist_length 10s;

添加转码和resize后的配置参见: nginx_with_resize_rtmp_hls_record_gop_hls_fragment.conf

ffmpeg调节横竖屏resize

之前ffmpeg的命令中,scale是按照宽为固定值来进行resize的。这种配置适用于横屏的情况,如果源视频是竖屏拍摄的,按照宽来进行缩放,会扩大高的长度,造成尺寸的不准确。
拿源视频尺寸是720x1280的竖屏为例。hd720的尺寸就会被扩展成1280x2274

我们可以在ffmpeg-vf参数中添加横竖屏的判断,来生成准确的resize尺寸。
判断依据是检查输入视频的宽和高,如果宽(iw)大于高(ih), 那么设置宽为固定值,高按照比例缩放。反之,就设置高为固定值,宽按照比例缩放。

1
2
3
4
5
6
exec /usr/bin/ffmpeg -i rtmp://localhost/$app/$name -async 1 -vsync -1
-c:v libx264 -c:a aac -b:v 256k -b:a 128k -ac 2 -g 30 -vf "scale='if(gt(iw,ih),480,trunc(oh*a/2)*2)':'if(gt(iw,ih),trunc(ow/a/2)*2,480)'" -tune zerolatency -preset veryfast -crf 23 -f flv rtmp://localhost/vod/$name_low
-c:v libx264 -c:a aac -b:v 768k -b:a 128k -ac 2 -g 30 -vf "scale='if(gt(iw,ih),720,trunc(oh*a/2)*2)':'if(gt(iw,ih),trunc(ow/a/2)*2,720)'" -tune zerolatency -preset veryfast -crf 23 -f flv rtmp://localhost/vod/$name_mid
-c:v libx264 -c:a aac -b:v 1024k -b:a 128k -ac 2 -g 30 -vf "scale='if(gt(iw,ih),960,trunc(oh*a/2)*2)':'if(gt(iw,ih),trunc(ow/a/2)*2,960)'" -tune zerolatency -preset veryfast -crf 23 -f flv rtmp://localhost/vod/$name_high
-c:v libx264 -c:a aac -b:v 1920k -b:a 128k -ac 2 -g 30 -vf "scale='if(gt(iw,ih),1280,trunc(oh*a/2)*2)':'if(gt(iw,ih),trunc(ow/a/2)*2,1280)'" -tune zerolatency -preset veryfast -crf 23 -f flv rtmp://localhost/vod/$name_hd720
-c copy -f flv rtmp://localhost/vod/$name_src;

此时,如果源视频尺寸是720x1280的竖屏,hd720的尺寸还是720x1280`。

添加转码和resize后的配置参见: nginx_with_resize_rtmp_hls_record_gop_hls_fragment_vf.conf

HLS中生成的m3u8中ts数量

hls_fragment参数,在文档上说设置的是hls片段的大小,可设置了hls_fragment后,m3u8上显示还是8秒一个片段。
HLS生成的m3u8中ts的个数=hls_playlist_length / hls_fragment
比如:

1
2
hls_fragment 1s;
hls_playlist_length 15s;

那么此时,m3u8中ts的个数就是15个。
注意: 即使因为没有关键帧,导致hls_fragment没起作用,实际hls的片段时间是5s或者是8s。ts的数量也还是hls_playlist_length / hls_fragment

访问allow和deny

nginx-rtmp-module提供了基础的基于ip地址的访问控制,参见文档https://github.com/arut/nginx-rtmp-module/wiki/Directives#access
有两个关键字allowdeny

  • allow的语法为: allow [play|publish] address|subnet|all
  • deny的语法为: deny [play|publish] address|subnet|all

其中的play是播放,publish是接收推流的意思。可以有多条allow/deny的规则,匹配规则是逐个check,直到匹配到符合的规则为止。

几个访问控制的例子:

  1. 允许所有地址publish

    1
    allow publish all;
  2. 只允许本地publish

    1
    2
    allow publish 127.0.0.1;
    deny publish all;
  3. 不允许play

    1
    deny play all;

rtmp流为什么播放时也会有延迟

RTMP流理论上只有网络传输延迟,但是客户端播放的时候经常会感觉到有几秒的延迟。
抛去网络因素,RTMP流观看的延迟,主要是客户端缓存导致的。为了避免网络抖动导致的播放卡顿,很多RTMP播放器都会在收到一段时间的RTMP流后才开始播放。这样就导致了播放的延迟。
ffmpeg自带的ffplay,可以通过设置nobuffer来禁用缓存,降低播放的延迟。

本地环境中的Mac使用ffplay连虚拟机中的流媒体服务器,添加nobuffer参数后,播放就和rtmp流基本同步了。

1
ffplay -fflags nobuffer 'rtmp://192.168.168.199/live/room888'

几个参考资料:

m3u8文件会时不时的消失

hls_fragmenthls_playlist_length的值太接近,导致只有一到两个片段时候,m3u8可能会时不时的消失。
比如,如下配置时,就可能会导致m3u8文件丢失。

1
2
hls_fragment 1s;
hls_playlist_length 1s;

此时需要将hls_playlist_length设置大一点,比如hls_playlist_length 3s;。这时m3u8文件就不会消失了。

相关阅读

留言