使用nginx搭建音视频点播服务器

目录

1 音视频技术

1.1 流媒体

  在网络上传输音、视频等多媒体信息,主要有下载和流式传输两种方案。下载的方式对于体积较大的音、视频数据而言,在使用前下载整个媒体文件耗时很大;流媒体(streaming media)传输提供了一种低延时的方式来避免这个问题,流媒体技术是指将多媒体数据压缩后,经过网络分段发送,在网上即时传输音、视频数据供用户播放的一种技术。

1.2 点播与直播

  点播(VOD,video on demand)即根据用户需求播放,音视频内容一般已经存放在服务器上了,不同于先下载再播放,点播要求即点即播,各大音视频网站(喜马拉雅、优酷、爱奇艺等)提供的服务就是VOD服务。
  直播(Live)需要对音视频内容实时录制,在录制同时以“流”的形式推送到服务器,服务器在收到用户的播放请求后,会将媒体流传输到用户的播放器进行播放,并对延迟有较高的要求,各大直播平台(斗鱼、虎牙等)提供的服务就是直播服务。

1.3 HLS流媒体协议

  HLS(HTTP Live Streaming)自适应码率流媒体传输协议,来自Apple公司,该协议基于Http协议,HLS诞生之初旨在能够从iPhone中删除flash,如今已成为使用最广泛的协议。HLS的优缺点也比较明显。

HLS的优点:
  Html5能够直接支持HLS,有浏览器就能播放,不需要安装额外的app,不用考虑防火墙或者代理问题;HLS也支持最新的H.265编解码器,H.265编码的质量要比H.264高很多。
HLS的缺点:
  因为HLS基本是采用了点播切片的方式实现的直播,在直播上相对于其它协议有较高的延迟(5~8秒延迟)。

除了HLS协议之外,相关的协议还有RTP/RTCP、RTSP、RTMP、MSS、DASH、HDS、WEBRTC、RIST、SRT等……它们都有各自的优缺点。

HLS的原理:
  首先将一个完整音视频分成多个ts格式音视频文件切片,并生成m3u8索引文件,用户下载m3u8索引文件,通过m3u8文件中每段ts切片的索引地址来播放具体的ts切片,在m3u8中可以指定播放相关的参数。

1.4 CDN

  CDN(Content Delivery Network)内容分发网络,是为了改善网络服务质量而产生的,它是构建在现有网络基础之上的智能虚拟网络,通过负载均衡、内容分发、调度等完成资源就近获取的目标。
  CDN的基本原理是采用各种缓存服务器,将这些缓存服务器分布到用户访问相对集中的地区或网络中,在用户访问网站时,利用全局负载技术将用户的访问指向距离最近的工作正常的缓存服务器上,由缓存服务器直接响应用户请求,使用户就近获取所需内容,降低网络拥塞,提高用户访问响应速度和命中率,CDN网络的诞生大大地改善了互联网的服务质量。
  在互联网上的流媒体服务为了实现更优质的用户体验,一般都需要经过CDN节点进行内容分发。点播、直播技术的选择需要考虑大环境下主流CDN服务供应商能够提供的服务支持,各家对协议的支持也不一样(像阿里云CDN支持RTMP输入,RTMP、FLV、HLS输出),点播需要考虑输出,直播需要考虑输入与输出两端。

2 搭建点播服务(音视频同理)

2.1 nginx服务器

nginx是一个代理服务器,首先需要在Linux上搭建nginx服务,具体的流程可以参考nginx的安装流程[1],搭建视频点播服务器会用到nginx-vod-module模块,按照安装流程添加该模块到nginx中,搭建点播服务使用的具体环境如下:

系统版本:CentOS Linux release 7.9 2009(Core)
nginx版本:1.20.1
nginx-vod-module版本:1.28

2.2 nginx-vod-module模块

nginx-vod-module模块是基于nginx来提供VOD(video on demand)服务的第三方模块,它支持基于DASH、HDS、HLS、MSS的点播服务搭建。

nginx-vod-module下载地址:https://github.com/kaltura/nginx-vod-module/archive/refs/tags/1.28.tar.gz

在nginx进行安装配置的过程中,需要使用--add-module=选项来添加第三方模块一起进行编译,相关的步骤可以参考模块的github网址:

nginx-vod-module的github网址:https://github.com/kaltura/nginx-vod-module

在配置nginx进行编译的时候使用的配置参数如下:

./configure --prefix=/web/webserver/nginx-vod-hls --with-http_stub_status_module --with-http_gzip_static_module --with-http_gunzip_module --with-file-aio --with-threads --with-cc-opt="-O3" --with-http_ssl_module --with-openssl-opt=enable --with-http_mp4_module --with-stream --add-module=../nginx-vod-module-master

配置中主要是附加了一些常规模块,打开了一些配置开关,其中:

--with-file-aio 异步io支持,强烈推荐,只在local、mapped模式下
--with-threads (nginx 1.7.11+) 支持使用线程池异步打开文件(要求在nginx.conf中配置vod_open_file_thread_pool),只在local、mapped模式下
--with-cc-opt="-O3" 附加编译器优化,减少视频解析和帧处理的耗时

配置、编译、安装都完成之后,即可在nginx的配置文件中使用nginx-vod-module模块提供的关键字了。

2.3 配置nginx点播服务

2.3.1 local模式

2.3.1.1 常规配置

在nginx的配置文件中,添加对应的location配置:

location /vod {
    vod hls; # 协议使用hls模式
    vod_mode local; # 访问模式指定为local模式
    
    vod_align_segments_to_key_frames on; # 每个切片以关键帧开头
    vod_manifest_segment_durations_mode accurate; # 精确显示每个切片的长度
    
    # 解决浏览器跨域问题
    add_header Access-Control-Allow-Headers '*';
    add_header Access-Control-Expose-Headers 'Server,range,Content-Length,Content-Range';
    add_header Access-Control-Allow-Methods 'GET, HEAD, OPTIONS';
    add_header Access-Control-Allow-Origin '*';

    alias /media; # 视频文件路径
}

nginx.conf文件修改后,nginx需要reload才能生效配置。

在服务器上对应的视频文件目录结构:

/media/example0001.mp4

URL播放地址(假设nginx配置在ip为192.168.192.128的地址上):

http://192.168.192.128/vod/example0001.mp4/index.m3u8

使用相应的html5播放器或者本地播放器打开,即可观看该地址的视频点播。若使用本地播放器,推荐使用VLC media player

nginx-vod-module会根据URL动态生成对应的m3u8文件,其内容如下:

#EXTM3U
#EXT-X-TARGETDURATION:17
#EXT-X-ALLOW-CACHE:YES
#EXT-X-PLAYLIST-TYPE:VOD
#EXT-X-VERSION:3
#EXT-X-MEDIA-SEQUENCE:1
#EXTINF:10.000,
http://192.168.192.128/vod/example0001.mp4/seg-1-v1-a1.ts
#EXTINF:10.000,
http://192.168.192.128/vod/example0001.mp4/seg-2-v1-a1.ts
#EXTINF:10.000,
http://192.168.192.128/vod/example0001.mp4/seg-3-v1-a1.ts
……
……
……
#EXTINF:7.760,
http://192.168.192.128/vod/example0001.mp4/seg-347-v1-a1.ts
#EXT-X-ENDLIST

通过对nginx进行配置可以控制m3u8文件的生成规则,以此来控制切片的大小、状态等播放相关的属性,具体可以查看nginx-vod-module模块的文档[2]

2.3.1.2 local模式下的多码率自适应

使用常规配置,仅更改访问的URL播放地址即可支持多码率自适应功能,HLS根据用户当前可用带宽的大小自适应匹配对应码率的视频。

在服务器上对应的视频文件目录结构(后缀low表示同一视频的低清晰度版本):

/media/example0001.mp4
/media/example0001_low.mp4

URL播放地址:

http://192.168.192.128/vod/example,0001,0001_low,.mp4.urlset/master.m3u8

多码率自适应其实是多个URL组合的结构,上述master.m3u8表示以下2个index.m3u8的组合:

http://192.168.192.128/vod/example0001.mp4/index.m3u8
http://192.168.192.128/vod/example0001_low.mp4/index.m3u8

nginx-vod-module会根据master指定的多个视频自动计算配置适合的带宽,master.m3u8文件的内容如下:

#EXTM3U
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=613135,RESOLUTION=1280x720,FRAME-RATE=25.000,CODECS="avc1.640029,mp4a.40.2"
http://192.168.192.128/vod_json/mapped.json/index-f1-v1-a1.m3u8
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=226233,RESOLUTION=760x428,FRAME-RATE=25.000,CODECS="avc1.42c01e,mp4a.40.2"
http://192.168.192.128/vod_json/mapped.json/index-f2-v1-a1.m3u8

#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=193615,RESOLUTION=1280x720,CODECS="avc1.640029",URI="http://192.168.192.128/vod_json/mapped.json/iframes-f1-v1-a1.m3u8"
#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=58630,RESOLUTION=760x428,CODECS="avc1.42c01e",URI="http://192.168.192.128/vod_json/mapped.json/iframes-f2-v1-a1.m3u8"

index后面跟随的v1表示第一条视频轨道,a1表示第一条音轨。

2.3.2 mapped模式

  mapped模式使用json文件来对视频进行配置,可以把附加在URL中的各种参数放在对应的json文件里进行配置。

2.3.2.1 常规配置

在nginx的配置文件中,添加对应的location配置:

location /vod_json {
    vod hls; # 协议使用hls模
    vod_mode mapped; # 访问模式指定为mapped模式

    vod_align_segments_to_key_frames on; # 每个切片以关键帧开头
    #vod_manifest_segment_durations_mode accurate; # 精确显示每个切片的长度
    
    # 解决浏览器跨域问
    add_header Access-Control-Allow-Headers '*';
    add_header Access-Control-Expose-Headers 'Server,range,Content-Length,Content-Range';
    add_header Access-Control-Allow-Methods 'GET, HEAD, OPTIONS';
    add_header Access-Control-Allow-Origin '*';

    alias /media/cjcwgl; # 视频文件路径
}

发现一个Bug:在mapped模式下配置vod_manifest_segment_durations_mode参数会导致m3u8的最后一个切片的#EXTINF时长异常地长。

在服务器上对应的视频文件目录结构:

/media/cjcwgl/hdh/cwgl0101.mp4

json文件路径为:

/media/cjcwgl/simple.json

simple.json文件的内容:

{
    "sequences": [
        {
            "clips": [
                {
                    "type": "source",
                    "path": "/media/cjcwgl/hdh/cwgl0101.mp4"
                }
            ]
        }
    ]
}

URL播放地址:

http://192.168.192.128/vod_json/simple.json/index.m3u8

2.3.2.2 mapped模式下的多码率自适应

在服务器上对应的视频文件目录结构(hdh目录下为高清版本,hdk目录下为普通版本):

/media/cjcwgl/hdh/cwgl0101.mp4
/media/cjcwgl/hdk/cwgl0101.mp4

json文件路径为:

/media/cjcwgl/mapped.json

mapped.json文件的内容:

{
    "sequences": [
        {
            "clips": [
                {
                    "type": "source",
                    "path": "/media/cjcwgl/hdh/cwgl0101.mp4"
                }
            ]
        },
        {
            "clips": [
                {
                    "type": "source",
                    "path": "/media/cjcwgl/hdk/cwgl0101.mp4"
                }
            ]
        }
    ]
}

json文件修改后,重新访问URL即可生效配置,不需要对nginx进行reload。

URL播放地址:

http://192.168.192.128/vod_json/mapped.json/master.m3u8

2.3.2.3 HLS协议的多码率自适应逻辑

  HLS的多码率自适应逻辑是依据前一个ts切片的下载速度来作为是否切换码率的依据。

示例:

#EXTM3U
#EXT-X-STREAM-INF:……BANDWIDTH=700000
http://……/index-f1-v1-a1.m3u8
#EXT-X-STREAM-INF:……BANDWIDTH=1300000
http://……/index-f2-v1-a1.m3u8
#EXT-X-STREAM-INF:……BANDWIDTH=2300000
http://……/index-f3-v1-a1.m3u8

  上述master.m3u8包含三个码率500K,900K和1400K相应的index.m3u8,在播放视频的时候, 第一个切片取的是master.m3u8中排在首位的index.m3u8中的首个ts切片。
  当配置了码率自适应之后, 在每次下载完一个ts切片的时候,会依据该切片的下载速度的80%来决定是否要切换码率, 如果当前切片的下载速度分别为:
1M = 那么其80%速度为 800K, 低于1300K, 那么下一个切片会下载500K码率;
2M = 其80%速度为 1600K, 高于1300K低于2300K, 下一个切片会下载1300K码率;
3M = 其80%速度为 2400K, 高于2300K, 下一个切片会下载2300K码率。

2.3.2.3 mapped模式下的倍速播放

在服务器上对应的视频文件目录结构:

/media/cjcwgl/hdh/cwgl0101.mp4

json文件路径为:

/media/cjcwgl/mapped2.json

mapped2.json文件的内容:

{
    "sequences": [
        {
        "clips": [
            {
                "type": "rateFilter",
                "rate": 2.0,
                "source": {
                    "type": "source",
                    "path": "/media/cjcwgl/hdh/cwgl0101.mp4"
                    }
                }
            ]
        }
    ]
}

URL播放地址:

http://192.168.192.128/vod_json/mapped2.json/index.m3u8

如果倍速播放的各种配置都正确,但是无法播放,在./logs/error.log中显示filter no supported的相关信息,则很有可能是缺少libavcodec与libavfilter库的支持。

2.3.2.4 安装libavcodec与libavfilter库支持

如果在点播服务中需要支持倍速输出、视频与音轨的分离等操作,需要额外的库libavcodec与libavfilter的支持,它们都基于ffmpeg,通过安装ffmpeg可提供相关的库支持,步骤如下:

安装libfdk-aac:

安装包获取地址:https://github.com/mstorsjo/fdk-aac/releases
安装包名:fdk-aac-2.0.1.tar.gz
解压:tar -zxvf fdk-aac-2.0.1.tar.gz
进目录:cd fdk-aac-2.0.1
生成配置:./autogen.sh

如果出现错误:“./autogen.sh:行2: autoreconf: 未找到命令”,需要先安装autoconf、automake、libtool:
在centos7.9下可以使用yum安装:
yum install autoconf automake libtool
在centos6.5下如果使用yum无法成功安装,则需要进行手动安装:

安装autoconf
下载:wget http://mirrors.kernel.org/gnu/autoconf/autoconf-2.65.tar.gz
解压:tar -xzvf autoconf-2.65.tar.gz
进目录:cd autoconf-2.65
配置:./configure

可能在autocon的安装过程中还需要perl,安装perl:
下载:wget https://www.cpan.org/src/5.0/perl-5.34.0.tar.gz
解压:tar -xzvf perl-5.34.0.tar.gz
进目录:cd perl-5.34.0
配置:./Configure -des -Dprefix=/usr/local/perl
编译:make
安装:make install

重新进行配置:./configure
编译:make
安装:make install

安装automake
下载:wget http://mirrors.kernel.org/gnu/automake/automake-1.11.tar.gz
解压:tar -xzvf automake-1.11.tar.gz
进目录:cd automake-1.11
配置:./configure
编译:make
安装:make install

安装libtool
下载:wget http://mirrors.kernel.org/gnu/libtool/libtool-2.2.6b.tar.gz
解压:tar -xzvf libtool-2.2.6b.tar.gz
进目录:cd libtool-2.2.6b
配置:./configure
编译:make
安装:make install

重新生成配置:./autogen.sh
配置:./configure --enable-shared
编译:make
安装:make install

安装ffmpeg:

安装包获取地址:https://ffmpeg.org/download.html#releases
安装包:ffmpeg-4.4.tar.gz
配置:./configure --enable-shared --enable-libfdk-aac

如果出现提示:“nasm/yasm not found or too old. Use --disable-x86asm for a crippled build.”,则需要安装nasm或者yasm其中之一:
在centos7.9下的可以使用yum进行安装:
yum install nasm
在centos6.5下如果无法使用yum安装,则需要手动安装:
下载:wget https://www.nasm.us/pub/nasm/releasebuilds/2.13.03/nasm-2.13.03.tar.gz
解压:tar -xvf nasm-2.13.03.tar.gz
进目录:cd nasm-2.13.03
配置:./configure
编译:make
安装:make install

如果出现警告:“WARNING: using libfdk without pkg-config”,则需要配置PKG_CONFIG_PATH如下:
export PKG_CONFIG_PATH=/usr/local/lib/pkgconfig
查看是否配置成功:
echo $PKG_CONFIG_PATH

然后重新配置:./configure --enable-shared --enable-libfdk-aac
编译:make
安装:make install

配置共享动态库:

编辑配置文件命令:vi /etc/ld.so.conf
在末尾加入库路径:/usr/local/lib
使配置生效命令:ldconfig

配置环境变量:

编辑文件:vi /etc/profile
在末尾加入:export PATH="/usr/local/lib:$PATH"
使配置文件生效:source /etc/profile

测试ffmpeg是否安装成功:

在命令行执行:ffmpeg
如果安装成功,会出现如下信息:

ffmpeg version 4.4 Copyright (c) 2000-2021 the FFmpeg developers
  built with gcc 4.8.5 (GCC) 20150623 (Red Hat 4.8.5-44)
  configuration: --enable-shared --enable-libfdk-aac
  libavutil      56. 70.100 / 56. 70.100
  libavcodec     58.134.100 / 58.134.100
  libavformat    58. 76.100 / 58. 76.100
  libavdevice    58. 13.100 / 58. 13.100
  libavfilter     7.110.100 /  7.110.100
  libswscale      5.  9.100 /  5.  9.100
  libswresample   3.  9.100 /  3.  9.100
Hyper fast Audio and Video encoder
usage: ffmpeg [options] [[infile options] -i infile]... {[outfile options] outfile}...

信息中显示libavcodec与libavfilter库的相关版本。

安装成功ffmpeg之后,就有了libavcodec与libavfilter库支持,此时需要重新编译nginx,这样才能把这两个库编译进nginx,即重新执行nginx的configure->make->make install三个步骤。

./configure ……配置过程中,如果nginx能够正确找到libavcodec与libavfilter库,则会显示如下信息片段:

……
checking for libavcodec ... found
checking for libswscale ... found
checking for libavfilter ... found
……

配置成功后,进行编译make,最后安装make install,至此已经成功在nginx中添加了libavcodec与libavfilter库支持。

在安装之前如果nginx已经在运行中,需要先./nginx -s stop,否则可能出现文件占用的问题。

2.3.3 完整的nginx.conf文件

nginx的完整配置文件,/nginx安装路径/conf/nginx.conf

worker_processes  1;

events {
    worker_connections  1024;
}

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

    sendfile        on;

    keepalive_timeout  65;

    server {
        listen       80;
        server_name  localhost;

        autoindex on;

        location /vod {
            vod hls; # 开启vod模块
            vod_mode local; # 访问模式指定为local
            
            vod_align_segments_to_key_frames on; # 每个切片以关键帧开头
            vod_manifest_segment_durations_mode accurate; # 精确显示每个切片的长度
            
            add_header Access-Control-Allow-Headers '*';
            add_header Access-Control-Expose-Headers 'Server,range,Content-Length,Content-Range';
            add_header Access-Control-Allow-Methods 'GET, HEAD, OPTIONS';
            add_header Access-Control-Allow-Origin '*';

            alias /media;
        }

        location /vod_json {
            vod hls;
            vod_mode mapped;

            vod_align_segments_to_key_frames on; # 每个切片以关键帧开头
            
            add_header Access-Control-Allow-Headers '*';
            add_header Access-Control-Expose-Headers 'Server,range,Content-Length,Content-Range';
            add_header Access-Control-Allow-Methods 'GET, HEAD, OPTIONS';
            add_header Access-Control-Allow-Origin '*';

            alias /media/cjcwgl;
        }

        location /data {
            alias /media;
        }

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

3 关于nginx-vod-module模块

  模块里还有很多功能,比如加密传输、数据缓存、代理上游服务等,但文档里只提供了几个基本的例子,遇到问题的话可以在github的issues[3]里寻找相应的问题。


  1. nginx安装流程: https://blog.csdn.net/SeeDoubleU/article/details/121727292 ↩︎

  2. nginx-vod-module文档: https://github.com/kaltura/nginx-vod-module/blob/master/README.md ↩︎

  3. github issues: https://github.com/kaltura/nginx-vod-module/issues ↩︎

上一篇:Github作为maven私服仓库用


下一篇:js播放m3u8格式视频