源码编译nginx并添加RTMP模块¶
先决条件¶
- nginx源代码,需要从GitHub Release下载,从nginx官网下载的源代码在后续编译时会报如下错误:
NMAKE : fatal error U1073: don't know how to make 'src/os/win32/ngx_win32_config.h'
- openssl源代码,可以从GitHub Release下载
- zlib源代码,GitHub仓库:https://github.com/madler/zlib
- pcre源代码,官网提供各版本的下载链接:https://ftp.pcre.org/pub/pcre/
- RMTP插件:https://github.com/arut/nginx-rtmp-module
- MSYS,可以通过MinGW Installer进行安装
- Visual Studio,需要安装C++编译器
- NASM
- Perl>=5.30
本次编译使用的环境:
- Windows 19042.662
- Visual Studio 2019
- Visual Studio Build Tools 2017
- Windows 10 SDK 10.0.17763.0
- nginx 1.19.5
- openssl 1.1.1g
- zlib 1.2.11
- pcre 8.44
- perl 5.32 (Strawberry)
- nasm 2.15.05
- nmake 14.27.29112.0
- MSVC(cl) 19.27.29112
将包含perl.exe
、nasm.exe
、sed.exe
的目录添加到PATH环境变量。
准备工作¶
启动MSYS,cd
到nginx的源代码目录,切换到C盘根目录的代码如下:
cd /c/
执行auto/configure
,根据nginx官网提供的参数进行,添加``参数,具体命令如下:
auto/configure \
--with-cc=cl \
--with-debug \
--prefix=. \
--conf-path=conf/nginx.conf \
--pid-path=logs/nginx.pid \
--http-log-path=logs/access.log \
--error-log-path=logs/error.log \
--sbin-path=nginx.exe \
--http-client-body-temp-path=temp/client_body_temp \
--http-proxy-temp-path=temp/proxy_temp \
--http-fastcgi-temp-path=temp/fastcgi_temp \
--http-scgi-temp-path=temp/scgi_temp \
--http-uwsgi-temp-path=temp/uwsgi_temp \
--with-cc-opt=-DFD_SETSIZE=1024 \
--with-pcre=pcre-8.44 \
--with-zlib=zlib \
--with-openssl=openssl \
--with-openssl-opt=no-asm \
--with-http_ssl_module \
--add-module=nginx-rtmp-module
注意此处--with-pcre
、--with-zlib
、--with-openssl
参数中提供的路径需要与PCRE、zlib、openssl的源代码路径分别对应。
执行完毕后生成Makefile文件。32位环境下可以直接开始编译,64位环境下需要对auto\lib\openssl\makefile.msvc
改为如下代码
# Copyright (C) Igor Sysoev
# Copyright (C) Nginx, Inc.
all:
cd $(OPENSSL)
perl Configure VC-WIN64A no-shared \
--prefix="%cd%/openssl" \
--openssldir="%cd%/openssl/ssl" \
$(OPENSSL_OPT)
if exist ms\do_win64a.bat ( \
ms\do_win64a \
&& $(MAKE) -f ms\nt.mak \
&& $(MAKE) -f ms\nt.mak install \
) else ( \
$(MAKE) \
&& $(MAKE) install_sw \
)
将VC-WIN32
替换为VC-WIN64A
,所有的ms\do_ms.bat
替换为ms\do_win64a.bat
。
MSVC编译¶
编辑如下源代码文件:
nginx-rtmp-module/ngx_rtmp_flv_module.c
,定位到如下代码块:
509行:
ngx_rtmp_prepare_message(s, &h, ctx->msg_mask & (1 << h.type) ?
&lh : NULL, out);
521行:
ctx->msg_mask |= (1 << h.type);
将其中的(1 << h.type)
改为(unsigned long)(1 << h.type)
。
nginx-rtmp-module/ngx_rtmp_core_module.c
,定位到如下代码块:
611行:
struct sockaddr *sa;
将此处的变量名sa
改为sa2
,并将此后所有的sa
变量改为sa2
。
启动x64 Native Tools Command Prompt for VS 2019,切换到源代码目录,执行nmake
启动编译。输出文件为objs/nginx.exe
nginx配置¶
将objs/nginx.exe
复制一份。在所在目录下创建如下文件结构:
.
├─ nginx.exe
├─ conf/
│ └─ nginx.conf
├─ html
│ └─ index.xsl
├─ logs
└─ temp
其中,html/index.xsl
写入如下内容
html/index.xsl文件内容
<?xml version="1.0" encoding="utf-8" ?>
<!--
Copyright (C) Roman Arutyunyan
-->
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<html>
<head>
<title>RTMP statistics</title>
</head>
<body>
<xsl:apply-templates select="rtmp"/>
<hr/>
Generated by <a href='https://github.com/arut/nginx-rtmp-module'>
nginx-rtmp-module</a> <xsl:value-of select="/rtmp/nginx_rtmp_version"/>,
<a href="http://nginx.org">nginx</a> <xsl:value-of select="/rtmp/nginx_version"/>,
pid <xsl:value-of select="/rtmp/pid"/>,
built <xsl:value-of select="/rtmp/built"/> <xsl:value-of select="/rtmp/compiler"/>
</body>
</html>
</xsl:template>
<xsl:template match="rtmp">
<table cellspacing="1" cellpadding="5">
<tr bgcolor="#999999">
<th>RTMP</th>
<th>#clients</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">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="/rtmp/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: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="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="../../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>
<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="../../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>
</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="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>
conf/nginx.conf
写入如下内容:
conf/nginx.conf文件内容
worker_processes 8;
error_log logs/error.log info;
events {
worker_connections 1024;
}
rtmp {
server {
listen 1935;
application live {
live on;
}
application hls {
live on;
hls on;
hls_path temp/hls;
hls_fragment 8s;
}
}
}
http {
server {
listen 8080;
root html;
location / {
rtmp_stat all;
rtmp_stat_stylesheet index.xsl;
}
location /index.xsl {
root html;
}
location /hls {
types{
application/vnd.apple.mpegurl m3u8;
video/mp2t ts;
}
alias temp/hls;
expires -1;
}
}
}
双击nginx.exe
即可启动服务器,服务器同时监听1935端口与8080端口
- 推流使用的RTMP服务器地址为:
rtmp://localhost:1935/live
- 访问
http://localhost:8080
可以查看服务器端的统计信息 - 客户端(如VLC等)可以访问
rtmp://localhost:1935/live
接收流,localhost
可以更改为本机的IP地址
使用命令.\nginx.exe -s stop
关闭服务器