工作中发现挺多直播CDN在实现httpflv拉流时都没有使用http chunk编码,而是直接使用no-content-length的做法。所以想自己搭建一个直播CDN支持 http chunk编码。
系统环境 Ubuntu 18.04.4 LTS
软件 nginx-1.18.0
nginx扩展模块 nginx-http-flv-module
nginx-http-flv-modulex下载
git clone https://github.com/winshining/nginx-http-flv-module.git
复制代码
nginx配置安装
./configure --add-module=/home/wanghao/worker/opensourcecode/nginx_module/nginx-http-flv-module
make -j 4 && make install
cd /usr/local/nginx/
复制代码
#user nobody;
worker_processes 1;
events {
worker_connections 1024;
}
http { ## http拉流的配置
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
server {
listen 80;
server_name www.wawa.com;
location /live {
flv_live on;
}
}
}
rtmp { ## rtmp推流的配置
server {
listen 1935;
application myapp{
live on;
record off;
}
}
}
复制代码
对于不知道文件长度的情况下(例如直播),nginx默认使用chunked_transfer_encoding来传输。 所以,配置文件中不用显示的去设置
Syntax: chunked_transfer_encoding on | off;
Default:chunked_transfer_encoding on;
Context: http, server, location
复制代码
官方连接请点击
ffmpeg -re -i q00307z84wz.321002.1.ts -f flv rtmp://192.168.116.130:1935/myapp/123
复制代码
q00307z84wz.321002.1.ts是一个5分钟的文件,则ffmpeg大概在5分钟后退出。
real 5m0.382s
user 0m22.750s
sys 0m8.141s
复制代码
填写正确的IP地址端口和流ID后进行拉流
# curl -L -v "http://192.168.116.130/live?port=1935&app=myapp&stream=123" -o 1.flv
* Trying 192.168.116.130...
* TCP_NODELAY set
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0* Connected to 192.168.116.130 (192.168.116.130) port 80 (#0)
> GET /live?port=1935&app=myapp&stream=123 HTTP/1.1
> Host: 192.168.116.130
> User-Agent: curl/7.58.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Server: nginx/1.18.0
< Date: Fri, 03 Jul 2020 13:49:52 GMT
< Content-Type: video/x-flv
< Transfer-Encoding: chunked
< Connection: keep-alive
< Expires: -1
<
{ [449 bytes data]
100 1068k 0 1068k 0 0 97k 0 --:--:-- 0:00:10 --:--:-- 117k
复制代码
可以在响应头中看到使用了http chunk协议,且没有content-length
< Transfer-Encoding: chunked
复制代码
继续修改nginx.conf,关闭http chunk编码
location /live {
flv_live on;
chunked_transfer_encoding off;
}
复制代码
使用curl拉流测试
# curl -L -v "http://192.168.116.130/live?port=1935&app=myapp&stream=123" -o 1.flv
* Trying 192.168.116.130...
* TCP_NODELAY set
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0* Connected to 192.168.116.130 (192.168.116.130) port 80 (#0)
> GET /live?port=1935&app=myapp&stream=123 HTTP/1.1
> Host: 192.168.116.130
> User-Agent: curl/7.58.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Server: nginx/1.18.0
< Date: Fri, 03 Jul 2020 13:59:43 GMT
< Content-Type: video/x-flv
< Connection: keep-alive
< Expires: -1
* no chunk, no close, no size. Assume close to signal end
<
{ [1504 bytes data]
100 668k 0 668k 0 0 98k 0 --:--:-- 0:00:06 --:--:-- 112k
复制代码
可以看到已经没有 "Transfer-Encoding: chunked" 的响应头部了,仔细观察,输出了一行英文
* no chunk, no close, no size. Assume close to signal end
复制代码
no size 就是没有content-length
no chunk 就是没有 Transfer-Encoding: chunked
Assume close to signal end 可以理解为直播服务器主动关闭socket就代表直播流结束了
但是HTTP协议标准是content-length和Transfer-Encodinge二选一,所以no-centent-length的方案是非标准的。
继续深入发现"Connection: keep-alive",一般在直播场景Connection都是close。 HTTP1.1默认使用keep-alive的方式,但是客户端可以在请求的头部中指定使用http短链接的方式。
root@PF1YTXHH-ZTB:/storage/log_live# curl -L -H "Connection:Close" -v "http://192.168.116.130/live?port=1935&app=myapp&stream=123" -o 1.flv
* Trying 192.168.116.130...
* TCP_NODELAY set
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0* Connected to 192.168.116.130 (192.168.116.130) port 80 (#0)
> GET /live?port=1935&app=myapp&stream=123 HTTP/1.1
> Host: 192.168.116.130
> User-Agent: curl/7.58.0
> Accept: */*
> Connection:Close # 客户端指定使用close的方式
>
< HTTP/1.1 200 OK
< Server: nginx/1.18.0
< Date: Tue, 07 Jul 2020 13:14:43 GMT
< Content-Type: video/x-flv
< Connection: close # 服务端也返回了close
< Expires: -1
<
{ [1637 bytes data]
100 236k 0 236k 0 0 89583 0 --:--:-- 0:00:02 --:--:-- 89583^C
复制代码
通过adb logcat抓取快手,斗鱼,虎牙等头部直播APP的日志后,找到拉流地址,自己本地用curl拉流也是发现有挺多流都是
no-content-length,Google下得知,使用这种非标准的no-centengt-length的原因是实现简单。
在拉流http chunk流的时候,使用tcpdump抓包,wireshark分析后,均没有看到http chunk协议,与网上其他教程不符合。还需要再探索下。
tcpdump -i lo host 192.168.116.130 -nnvv -w live2.pcap
复制代码
因为拉流端和nginx服务器同一台机器,则从本地环回口拉取(lo)抓包,192.168.116.130是本地的内网IP。