利用nginx和VLC&FLV自建直播服务器并实现播放
服务器环境:Centos
nginx-rtmp-module项目地址:https://github.com/arut/nginx-rtmp-module
方法参考:https://www.nginx.org.cn/article/detail/259
此方法需要使用nginx插件,通过对nginx本体的编译安装实现。
编译安装命令
./configure --user=nginx --group=nginx --prefix=/home/nginx-rtmp/rtmp --add-module=/home/nginx-rtmp/nginx-rtmp-module-1.2.2 \ make && make install
注释:
--add-module=/home/nginx-rtmp/nginx-rtmp-module-1.2.2 是选择此项目编译入NGINX程序
服务端配置文件:
worker_processes 1; events { worker_connections 1024; } rtmp { server { listen 1935; chunk_size 4096; application vod { play /opt/video/vod; } application live{ #第一处添加的直播字段 live on; } } } http { include mime.types; default_type application/octet-stream; sendfile on; keepalive_timeout 65; server { listen 80; server_name localhost; location /stat { #第二处添加的location字段。 rtmp_stat all; rtmp_stat_stylesheet html/stat.xsl; } location /stat.xsl { #第二处添加的location字段。 root /home/nginx-rtmp/rtmp; } location / { root html; index index.html index.htm; } error_page 500 502 503 504 /50x.html; location = /50x.html { root html; } } }
OBS推流地址:rtmp://127.0.0.1:1935/live
VLC网络播放地址:rtmp://127.0.0.1:1935/live
更新的技术方法:nginx-http-flv-module
可选择的:--add-module=/path/to/nginx-http-flv-module
项目地址:https://github.com/winshining/nginx-http-flv-module
中文README:https://github.com/winshining/nginx-http-flv-module/blob/master/README.CN.md
OBS推流地址:rtmp://127.0.0.1:1935/live/tdu
FLV观看拉流:http://127.0.0.1/live?app=live&stream=tdu
flv.js demo :http://bilibili.github.io/flv.js/demo/
nginx配置文件
worker_processes 1; #运行在 Windows 上时,设置为 1,因为 Windows 不支持 Unix domain socket error_log logs/error.log error; events { worker_connections 4096; } http { include mime.types; default_type application/octet-stream; keepalive_timeout 65; server { listen 80; location / { index index.html index.htm; } error_page 500 502 503 504 /50x.html; location = /50x.html { root html; } 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 头 } location /hls { types { application/vnd.apple.mpegurl m3u8; video/mp2t ts; } root /tmp; add_header 'Cache-Control' 'no-cache'; } location /dash { root /tmp; add_header 'Cache-Control' 'no-cache'; } location /stat { #推流播放和录制统计数据的配置 rtmp_stat all; rtmp_stat_stylesheet stat.xsl; } location /stat.xsl { root html; #指定 stat.xsl 的位置 } #如果需要 JSON 风格的 stat, 不用指定 stat.xsl #但是需要指定一个新的配置项 rtmp_stat_format #location /stat { # rtmp_stat all; # rtmp_stat_format json; #} location /control { rtmp_control all; #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; application live { 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; } } }
在访问http://127.0.0.1/stat统计页面时,如果出现stat.xsl报错404,则需要加入以下文件在html/html路径
来源:https://github.com/winshining/nginx-http-flv-module/blob/master/stat.xsl
stat.xsl:
<?xml version="1.0" encoding="utf-8" ?> <!-- Copyright (C) Roman Arutyunyan Copyright (C) Winshining --> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="/"> <html> <head> <title>HTTP-FLV statistics</title> </head> <body> <xsl:apply-templates select="http-flv"/> <hr/> Generated by <a href='https://github.com/winshining/nginx-http-flv-module'> nginx-http-flv-module</a> <xsl:value-of select="/http-flv/nginx_http_flv_version"/>, <a href="http://nginx.org">nginx</a> <xsl:value-of select="/http-flv/nginx_version"/>, pid <xsl:value-of select="/http-flv/pid"/>, built <xsl:value-of select="/http-flv/built"/> <xsl:value-of select="/http-flv/compiler"/> </body> </html> </xsl:template> <xsl:template match="http-flv"> <table cellspacing="1" cellpadding="5"> <tr bgcolor="#999999"> <th>HTTP-FLV</th> <th>#clients</th> <th colspan="2">Recorders</th> <th colspan="2">Server</th> <th colspan="4">Video</th> <th colspan="4">Audio</th> <th>In bytes</th> <th>Out bytes</th> <th>In bits/s</th> <th>Out bits/s</th> <th>State</th> <th>Time</th> </tr> <tr> <td colspan="2">Accepted: <xsl:value-of select="naccepted"/></td> <th bgcolor="#999999">lists</th> <th bgcolor="#999999">#count</th> <th bgcolor="#999999">port</th> <th bgcolor="#999999">index</th> <th bgcolor="#999999">codec</th> <th bgcolor="#999999">bits/s</th> <th bgcolor="#999999">size</th> <th bgcolor="#999999">fps</th> <th bgcolor="#999999">codec</th> <th bgcolor="#999999">bits/s</th> <th bgcolor="#999999">freq</th> <th bgcolor="#999999">chan</th> <td> <xsl:call-template name="showsize"> <xsl:with-param name="size" select="bytes_in"/> </xsl:call-template> </td> <td> <xsl:call-template name="showsize"> <xsl:with-param name="size" select="bytes_out"/> </xsl:call-template> </td> <td> <xsl:call-template name="showsize"> <xsl:with-param name="size" select="bw_in"/> <xsl:with-param name="bits" select="1"/> <xsl:with-param name="persec" select="1"/> </xsl:call-template> </td> <td> <xsl:call-template name="showsize"> <xsl:with-param name="size" select="bw_out"/> <xsl:with-param name="bits" select="1"/> <xsl:with-param name="persec" select="1"/> </xsl:call-template> </td> <td/> <td> <xsl:call-template name="showtime"> <xsl:with-param name="time" select="/http-flv/uptime * 1000"/> </xsl:call-template> </td> </tr> <xsl:apply-templates select="server"/> </table> </xsl:template> <xsl:template match="server"> <xsl:apply-templates select="application"/> </xsl:template> <xsl:template match="application"> <tr bgcolor="#999999"> <td> <b><xsl:value-of select="name"/></b> </td> </tr> <xsl:apply-templates select="live"/> <xsl:apply-templates select="play"/> <xsl:apply-templates select="recorders"/> </xsl:template> <xsl:template match="live"> <tr bgcolor="#aaaaaa"> <td> <i>live streams</i> </td> <td align="middle"> <xsl:value-of select="nclients"/> </td> </tr> <xsl:apply-templates select="stream"/> </xsl:template> <xsl:template match="play"> <tr bgcolor="#aaaaaa"> <td> <i>vod streams</i> </td> <td align="middle"> <xsl:value-of select="nclients"/> </td> </tr> <xsl:apply-templates select="stream"/> </xsl:template> <xsl:template match="recorders"> <tr bgcolor="#aaaaaa"> <td colspan="2">Recorders</td> <td> <a href=""> <xsl:attribute name="onclick"> var d=document.getElementById('<xsl:value-of select="../../port"/>-<xsl:value-of select="../../server_index"/>-<xsl:value-of select="../name"/>-recorders'); d.style.display=d.style.display=='none'?'':'none'; return false; </xsl:attribute> <i>config</i> </a> </td> <td align="middle"> <xsl:value-of select="count"/> </td> </tr> <tr style="display:none"> <xsl:attribute name="id"><xsl:value-of select="../../port"/>-<xsl:value-of select="../../server_index"/>-<xsl:value-of select="../name"/>-recorders</xsl:attribute> <td colspan="16"> <table cellspacing="1" cellpadding="5"> <tr> <th>Id</th> <th>Path</th> <th>Suffix</th> <th>Flags</th> <th>Max Size</th> <th>Max Frames</th> <th>Interval</th> <th>Unique</th> <th>Append</th> <th>Lock File</th> <th>Notify</th> </tr> <xsl:apply-templates select="recorder"/> </table> </td> </tr> </xsl:template> <xsl:template match="recorder"> <tr bgcolor="#cccccc"> <td> <xsl:value-of select="id"/> <xsl:if test="string-length(id) = 0"> [DEFAULT] </xsl:if> </td> <td><xsl:value-of select="path"/></td> <td><xsl:value-of select="suffix"/></td> <td> <xsl:if test="flags/video">video<br/></xsl:if> <xsl:if test="flags/audio">audio<br/></xsl:if> <xsl:if test="flags/manual">manual</xsl:if> </td> <td> <xsl:call-template name="showsize"> <xsl:with-param name="size" select="max_size"/> </xsl:call-template> </td> <td><xsl:value-of select="max_frames"/></td> <td> <xsl:call-template name="showinterval"> <xsl:with-param name="interval" select="interval"/> </xsl:call-template> </td> <td> <xsl:call-template name="binarystate"> <xsl:with-param name="value" select="unique"/> </xsl:call-template> </td> <td> <xsl:call-template name="binarystate"> <xsl:with-param name="value" select="append"/> </xsl:call-template> </td> <td> <xsl:call-template name="binarystate"> <xsl:with-param name="value" select="lock_file"/> </xsl:call-template> </td> <td> <xsl:call-template name="binarystate"> <xsl:with-param name="value" select="notify"/> </xsl:call-template> </td> </tr> </xsl:template> <xsl:template name="binarystate"> <xsl:param name="value"/> <xsl:choose> <xsl:when test="$value">on</xsl:when> <xsl:otherwise>off</xsl:otherwise> </xsl:choose> </xsl:template> <xsl:template match="stream"> <tr valign="top"> <xsl:attribute name="bgcolor"> <xsl:choose> <xsl:when test="active">#cccccc</xsl:when> <xsl:otherwise>#dddddd</xsl:otherwise> </xsl:choose> </xsl:attribute> <td> <a href=""> <xsl:attribute name="onclick"> var d=document.getElementById('<xsl:value-of select="../../../port"/>-<xsl:value-of select="../../../server_index"/>-<xsl:value-of select="../../name"/>-<xsl:value-of select="name"/>'); d.style.display=d.style.display=='none'?'':'none'; return false </xsl:attribute> <xsl:value-of select="name"/> <xsl:if test="string-length(name) = 0"> [EMPTY] </xsl:if> </a> </td> <td align="middle"> <xsl:value-of select="nclients"/> </td> <td align="middle"> - </td> <td align="middle"> - </td> <td align="middle"> <xsl:value-of select="../../../port"/> </td> <td align="middle"> <xsl:value-of select="../../../server_index"/> </td> <td> <xsl:value-of select="meta/video/codec"/> <xsl:value-of select="meta/video/profile"/> <xsl:value-of select="meta/video/level"/> </td> <td> <xsl:call-template name="showsize"> <xsl:with-param name="size" select="bw_video"/> <xsl:with-param name="bits" select="1"/> <xsl:with-param name="persec" select="1"/> </xsl:call-template> </td> <td> <xsl:apply-templates select="meta/video/width"/> </td> <td> <xsl:value-of select="meta/video/frame_rate"/> </td> <td> <xsl:value-of select="meta/audio/codec"/> <xsl:value-of select="meta/audio/profile"/> </td> <td> <xsl:call-template name="showsize"> <xsl:with-param name="size" select="bw_audio"/> <xsl:with-param name="bits" select="1"/> <xsl:with-param name="persec" select="1"/> </xsl:call-template> </td> <td> <xsl:apply-templates select="meta/audio/sample_rate"/> </td> <td> <xsl:value-of select="meta/audio/channels"/> </td> <td> <xsl:call-template name="showsize"> <xsl:with-param name="size" select="bytes_in"/> </xsl:call-template> </td> <td> <xsl:call-template name="showsize"> <xsl:with-param name="size" select="bytes_out"/> </xsl:call-template> </td> <td> <xsl:call-template name="showsize"> <xsl:with-param name="size" select="bw_in"/> <xsl:with-param name="bits" select="1"/> <xsl:with-param name="persec" select="1"/> </xsl:call-template> </td> <td> <xsl:call-template name="showsize"> <xsl:with-param name="size" select="bw_out"/> <xsl:with-param name="bits" select="1"/> <xsl:with-param name="persec" select="1"/> </xsl:call-template> </td> <td><xsl:call-template name="streamstate"/></td> <td> <xsl:call-template name="showtime"> <xsl:with-param name="time" select="time"/> </xsl:call-template> </td> </tr> <tr style="display:none"> <xsl:attribute name="id"> <xsl:value-of select="../../../port"/>-<xsl:value-of select="../../../server_index"/>-<xsl:value-of select="../../name"/>-<xsl:value-of select="name"/> </xsl:attribute> <td colspan="16" ngcolor="#eeeeee"> <table cellspacing="1" cellpadding="5"> <tr> <th>Id</th> <th>State</th> <th>Address</th> <th>Flash version</th> <th>Page URL</th> <th>SWF URL</th> <th>Dropped</th> <th>Timestamp</th> <th>A-V</th> <th>Time</th> </tr> <xsl:apply-templates select="client"/> </table> <xsl:apply-templates select="records"/> </td> </tr> </xsl:template> <xsl:template match="records"> <table cellspacing="1" cellpadding="5"> <tr> <th>Recorder</th> <th>State</th> <th>Epoch</th> <th>Time Shift</th> <th>File Name</th> <th>Time</th> <th>Size</th> <th>Frames</th> </tr> <xsl:apply-templates select="record"/> </table> </xsl:template> <xsl:template match="record"> <tr> <xsl:attribute name="bgcolor"> <xsl:choose> <xsl:when test="recording">#cccccc</xsl:when> <xsl:otherwise>#eeeeee</xsl:otherwise> </xsl:choose> </xsl:attribute> <td> <xsl:value-of select="recorder"/> <xsl:if test="string-length(recorder) = 0"> [DEFAULT] </xsl:if> </td> <td> <xsl:choose> <xsl:when test="recording">recording</xsl:when> <xsl:otherwise>idle</xsl:otherwise> </xsl:choose> </td> <td><xsl:value-of select="epoch"/></td> <td><xsl:value-of select="time_shift"/></td> <td><xsl:value-of select="file"/></td> <td> <xsl:call-template name="showtime"> <xsl:with-param name="time" select="time * 1000"/> </xsl:call-template> </td> <td> <xsl:call-template name="showsize"> <xsl:with-param name="size" select="size"/> </xsl:call-template> </td> <td><xsl:value-of select="nframes"/></td> </tr> </xsl:template> <xsl:template name="showtime"> <xsl:param name="time"/> <xsl:if test="$time > 0"> <xsl:variable name="sec"> <xsl:value-of select="floor($time div 1000)"/> </xsl:variable> <xsl:if test="$sec >= 86400"> <xsl:value-of select="floor($sec div 86400)"/>d </xsl:if> <xsl:if test="$sec >= 3600"> <xsl:value-of select="(floor($sec div 3600)) mod 24"/>h </xsl:if> <xsl:if test="$sec >= 60"> <xsl:value-of select="(floor($sec div 60)) mod 60"/>m </xsl:if> <xsl:value-of select="$sec mod 60"/>s </xsl:if> </xsl:template> <xsl:template name="showsize"> <xsl:param name="size"/> <xsl:param name="bits" select="0" /> <xsl:param name="persec" select="0" /> <xsl:variable name="sizen"> <xsl:value-of select="floor($size div 1024)"/> </xsl:variable> <xsl:choose> <xsl:when test="$sizen >= 1073741824"> <xsl:value-of select="format-number($sizen div 1073741824,'#.###')"/> T</xsl:when> <xsl:when test="$sizen >= 1048576"> <xsl:value-of select="format-number($sizen div 1048576,'#.###')"/> G</xsl:when> <xsl:when test="$sizen >= 1024"> <xsl:value-of select="format-number($sizen div 1024,'#.##')"/> M</xsl:when> <xsl:when test="$sizen >= 0"> <xsl:value-of select="$sizen"/> K</xsl:when> </xsl:choose> <xsl:if test="string-length($size) > 0"> <xsl:choose> <xsl:when test="$bits = 1">b</xsl:when> <xsl:otherwise>B</xsl:otherwise> </xsl:choose> <xsl:if test="$persec = 1">/s</xsl:if> </xsl:if> </xsl:template> <xsl:template name="showinterval"> <xsl:param name="interval"/> <xsl:if test="$interval > 0"> <xsl:variable name="sec"> <xsl:value-of select="floor($interval div 1000)"/> </xsl:variable> <xsl:if test="$sec >= 86400"> <xsl:value-of select="floor($sec div 86400)"/>d </xsl:if> <xsl:if test="$sec >= 3600"> <xsl:value-of select="(floor($sec div 3600)) mod 24"/>h </xsl:if> <xsl:if test="$sec >= 60"> <xsl:value-of select="(floor($sec div 60)) mod 60"/>m </xsl:if> <xsl:if test="$sec < 60"> <xsl:value-of select="$sec mod 60"/>s </xsl:if> </xsl:if> <xsl:if test="$interval = -1">Unset</xsl:if> </xsl:template> <xsl:template name="streamstate"> <xsl:choose> <xsl:when test="active">active</xsl:when> <xsl:otherwise>idle</xsl:otherwise> </xsl:choose> </xsl:template> <xsl:template name="clientstate"> <xsl:choose> <xsl:when test="publishing">publishing</xsl:when> <xsl:otherwise>playing</xsl:otherwise> </xsl:choose> </xsl:template> <xsl:template match="client"> <tr> <xsl:attribute name="bgcolor"> <xsl:choose> <xsl:when test="publishing">#cccccc</xsl:when> <xsl:otherwise>#eeeeee</xsl:otherwise> </xsl:choose> </xsl:attribute> <td><xsl:value-of select="id"/></td> <td><xsl:call-template name="clientstate"/></td> <td> <a target="_blank"> <xsl:attribute name="href"> http://apps.db.ripe.net/search/query.html?searchtext=<xsl:value-of select="address"/> </xsl:attribute> <xsl:attribute name="title">whois</xsl:attribute> <xsl:value-of select="address"/> </a> </td> <td><xsl:value-of select="flashver"/></td> <td> <a target="_blank"> <xsl:attribute name="href"> <xsl:value-of select="pageurl"/> </xsl:attribute> <xsl:value-of select="pageurl"/> </a> </td> <td><xsl:value-of select="swfurl"/></td> <td><xsl:value-of select="dropped"/></td> <td><xsl:value-of select="timestamp"/></td> <td><xsl:value-of select="avsync"/></td> <td> <xsl:call-template name="showtime"> <xsl:with-param name="time" select="time"/> </xsl:call-template> </td> </tr> </xsl:template> <xsl:template match="publishing"> publishing </xsl:template> <xsl:template match="active"> active </xsl:template> <xsl:template match="width"> <xsl:value-of select="."/>x<xsl:value-of select="../height"/> </xsl:template> </xsl:stylesheet>
(如果需要)
发表评论