NGINX-based Media Streaming Server
nginx-rtmp-module
Project blog
Wiki manual
Google group
Donation page (Paypal etc)
Features
- RTMP/HLS/MPEG-DASH live streaming
- RTMP Video on demand FLV/MP4, playing from local filesystem or HTTP
- Stream relay support for distributed streaming: push & pull models
- Recording streams in multiple FLVs
- H264/AAC support
- Online transcoding with FFmpeg
- HTTP callbacks (publish/play/record/update etc)
- Running external programs on certain events (exec)
- HTTP control module for recording audio/video and dropping clients
- Advanced buffering techniques to keep memory allocations at a minimum level for faster streaming and low memory footprint
- Proved to work with Wirecast, FMS, Wowza, JWPlayer, FlowPlayer, StrobeMediaPlayback, ffmpeg, avconv, rtmpdump, flvstreamer and many more
- Statistics in XML/XSL in machine- & human- readable form
- Linux/FreeBSD/MacOS/Windows
Build
./configure --add-module=/path/to/nginx-rtmp-module
make
make install
./configure --add-module=/path/to/nginx-rtmp-module --with-http_ssl_module
--with-debug
./configure --add-module=/path/to-nginx/rtmp-module --with-debug
Windows limitations
- execs
- static pulls
- auto_push
RTMP URL format
rtmp://rtmp.example.com/app[/name]
Multi-worker live streaming
Example nginx.conf
rtmp {
server {
listen 1935;
chunk_size 4000;
# TV mode: one publisher, many subscribers
application mytv {
# enable live streaming
live on;
# record first 1K of stream
record all;
record_path /tmp/av;
record_max_size 1K;
# append current timestamp to each flv
record_unique on;
# publish only from localhost
allow publish 127.0.0.1;
deny publish all;
#allow play all;
}
# Transcoding (ffmpeg needed)
application big {
live on;
# On every pusblished stream run this command (ffmpeg)
# with substitutions: $app/${app}, $name/${name} for application & stream name.
#
# This ffmpeg call receives stream from this application &
# reduces the resolution down to 32x32. The stream is the published to
# 'small' application (see below) under the same name.
#
# ffmpeg can do anything with the stream like video/audio
# transcoding, resizing, altering container/codec params etc
#
# Multiple exec lines can be specified.
exec ffmpeg -re -i rtmp://localhost:1935/$app/$name -vcodec flv -acodec copy -s 32x32
-f flv rtmp://localhost:1935/small/${name};
}
application small {
live on;
# Video with reduced resolution comes here from ffmpeg
}
application webcam {
live on;
# Stream from local webcam
exec_static ffmpeg -f video4linux2 -i /dev/video0 -c:v libx264 -an
-f flv rtmp://localhost:1935/webcam/mystream;
}
application mypush {
live on;
# Every stream published here
# is automatically pushed to
# these two machines
push rtmp1.example.com;
push rtmp2.example.com:1934;
}
application mypull {
live on;
# Pull all streams from remote machine
# and play locally
pull rtmp://rtmp3.example.com pageUrl=www.example.com/index.html;
}
application mystaticpull {
live on;
# Static pull is started at nginx start
pull rtmp://rtmp4.example.com pageUrl=www.example.com/index.html name=mystream static;
}
# video on demand
application vod {
play /var/flvs;
}
application vod2 {
play /var/mp4s;
}
# Many publishers, many subscribers
# no checks, no recording
application videochat {
live on;
# The following notifications receive all
# the session variables as well as
# particular call arguments in HTTP POST
# request
# Make HTTP request & use HTTP retcode
# to decide whether to allow publishing
# from this connection or not
on_publish http://localhost:8080/publish;
# Same with playing
on_play http://localhost:8080/play;
# Publish/play end (repeats on disconnect)
on_done http://localhost:8080/done;
# All above mentioned notifications receive
# standard connect() arguments as well as
# play/publish ones. If any arguments are sent
# with GET-style syntax to play & publish
# these are also included.
# Example URL:
# rtmp://localhost/myapp/mystream?a=b&c=d
# record 10 video keyframes (no audio) every 2 minutes
record keyframes;
record_path /tmp/vc;
record_max_frames 10;
record_interval 2m;
# Async notify about an flv recorded
on_record_done http://localhost:8080/record_done;
}
# HLS
# For HLS to work please create a directory in tmpfs (/tmp/hls here)
# for the fragments. The directory contents is served via HTTP (see
# http{} section in config)
#
# Incoming stream must be in H264/AAC. For iPhones use baseline H264
# profile (see ffmpeg example).
# This example creates RTMP stream from movie ready for HLS:
#
# ffmpeg -loglevel verbose -re -i movie.avi -vcodec libx264
# -vprofile baseline -acodec libmp3lame -ar 44100 -ac 1
# -f flv rtmp://localhost:1935/hls/movie
#
# If you need to transcode live stream use 'exec' feature.
#
application hls {
live on;
hls on;
hls_path /tmp/hls;
}
# MPEG-DASH is similar to HLS
application dash {
live on;
dash on;
dash_path /tmp/dash;
}
}
}
# HTTP can be used for accessing RTMP stats
http {
server {
listen 8080;
# 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 /path/to/stat.xsl/;
}
location /hls {
# Serve HLS fragments
types {
application/vnd.apple.mpegurl m3u8;
video/mp2t ts;
}
root /tmp;
add_header Cache-Control no-cache;
}
location /dash {
# Serve DASH fragments
root /tmp;
add_header Cache-Control no-cache;
}
}
}
Multi-worker streaming example
rtmp_auto_push on;
rtmp {
server {
listen 1935;
application mytv {
live on;
}
}
}
from https://github.com/arut/nginx-rtmp-module(https://github.com/AlexWoo/nginx-rtmp-module)
-----------------
使用Nginx 和 RTMP 模块搭建视频直播系统
原文: Setting Up Adaptive Streaming with Nginx by Licson。
最近我在为一个组织搭建视频直播系统。对于视频直播来说我是新手,经过一番调研,最终还是觉得Nginx + RTMP module是一个好的选择。
搭建这个系统还是很困难的。经过了好几天的测试和摸索,我得到了一种比较好的配置,值得给大家分享。
Recently, I’m working out a system to smoothly stream live events for an organization. That is pretty new to me and, after a bunch of research, found that Nginx with the RTMP module seems to be a good choice. There are many difficulties when setting all this up and after several days of testing, I found a good setting that is worth a post.
Setup Nginx and RTMP module
First, let’s get Nginx set up. In order to use the RTMP module, we need to compile that as an Nginx module. It would look something like this:
|
|
After all things are done, check whether nginx is compiled properly.
If you can see that Nginx RTMP is included, you can go to the next step. Before we proceed to configuring Nginx for live streaming, we should confirm what kind of resolution we should provide for live streams and how much hardware power you have.
Prerequisites
For converting live streams into several streams for adaptive streaming, you need to make sure your server have enough CPU for such workload. Otherwise, the live stream will suffer from continuous delays and/or server becomes unresponsive. I have spawn some EC2 c3.large and c3.xlarge instances, test with them and I found out their optimized CPU can handle such workload with ease. Something that also worth mention is about the I/O limits of the disks. If possible, store the HLS fragments generated to an high-speed SSD helps maintain smooth streaming experiences.
Then, you also need to think about what kind of resolutions you will be offering for adaptive streaming. Generally about 4-5 variants are good enough to provide great loading speeds for different network speeds and devices. Here’s my recommended list of variants used for live streaming:
- 240p Low Definition stream at 288kbps
- 480p Standard Definition stream at 448kbps
- 540p Standard Definition stream at 1152kbps
- 720p High Definition stream at 2048kbps
- Source resolution, source bitrate
Configuring nginx for live streaming
Here is my own nginx.conf with comments that you can have references on.
|
|
Then, configure your live encoder to use these settings to stream into the server:
- RTMP Endpoint: rtmp://yourserver/live/
- RTMP Stream Name: [Whatever name you like]
Finally, configure your player for live playback. The HLS URL would look like this:
http://yourserver/hls/[The stream name above].m3u8
Recommended encoder settings for live events
If you can adjust the encoder, the following settings can help to gain better experiences.
- Full HD Resolution (1920×1080) is recommended
- H.264 Main profile, with target bitrate of 4000Kbps, maximum 6000Kbps
- 25fps, 2 second keyframe interval
- AAC audio at 128Kbps, 44.1kHz sample rate
And that’s all! I hope you can enjoy doing live events with these techniques.
-------------------------------------------------------
PingOS
nginx-rtmp-module/rtmp/http-flv/hls/hls+/http-ts/pull/push/relay/upstream/rtsp/nginx https://pingos.io服务器功能
- 直播协议: RTMP、HTTP(S)-FLV、HTTP(S)-TS、HLS(支持HTTPS)、HLS+(支持HTTPS)、DASH(支持HTTPS)。
- 音视频编码: H264、H265、MP3、AAC。
- 直播录像: FLV文件格式和TS文件格式。
- GOP缓存: 实现秒开和内存复用。
- application支持通配符: “ * ”号通配符实现自动匹配推拉流时使用的application名字,无需累赘的配置。
- VHOST功能: 支持配置多个server域名。
- 控制台接口: 通过HTTP API接口控制推流、拉流以及录像过程。
- 配置动态加载: 修改配置文件后无需对nginx做任何操作就可读取最新配置。
- 流量计费: 通过配置自定义流量日志。
- 变量参数配置: 配置文件中使用变量。
- 进程间回源: 进程间相互拉流,解决了原生nginx-rtmp-module模块多进程拉流失败的问题。
- 集群化功能: 服务器间推拉流功能(http-flv、rtmp协议)。
- html5网页播放器: pingos-player播放器将持续兼容各浏览器平台,以及多种直播协议。
引导
快速安装
- 直接安装到系统
# 快速安装 git clone https://github.com/pingostack/pingos.git cd pingos ./release.sh -i # 启动服务 cd /usr/local/pingos/ ./sbin/nginx
操作说明
推流
播放地址
- rtmp 播放:rtmp://ip/live/流名
- http(s)-flv 播放:http(s)://ip/flv/流名
- hls 播放:http(s)://ip/hls/流名.m3u8
- hls+ 播放:http(s)://ip/hls2/流名.m3u8
- http(s)-ts 播放:http(s)://ip/ts/流名
直播流监控后台
访问地址:http://ip/rtmp_stat 通过该页面可以查看当前正在发生的推流和播放记录。
html5播放器
from https://github.com/pingostack/pingos访问地址: http://ip/h5player/flv 这个播放器是基于flv.js的网页播放器,可以实现无插件播放http-flv直播流。 一旦你能够访问这个页面,说明你的直播服务器已经成功搭建起来了。
----
A Dockerfile for nginx-rtmp-module + FFmpeg from source with basic settings for streaming HLS. Built on Alpine Linux. https://hub.docker.com/repository/doc…
👬 前言
最近帮朋友的公司部署了一套分流+水印的直播系统顺手打包成docker镜像,方便大家需要用到的时候开箱即用,不需要百度一些零碎的文章 也可做简单的直播服务,只需调整配置文件便可达到你的需求.需求:将直播流分流到两个云厂商的直播云,一个有水印,一个无水印。使用hls播放朋友需求的拓扑示意图:当前拓扑示意图(阿里云和腾讯云不方便放出推流和拉流地址,有兴趣的同学可以去申请玩一下)
🐳 docker-nginx-rtmp-ffmpeg
- Nginx 1.16.1(从源代码编译的稳定版本)
- nginx-rtmp-module 1.2.1(从源代码编译)
- ffmpeg 4.2.1(从源代码编译)
- 已配置好的nginx.conf
- 只支持1920*1080(如需支持其他分辨率可参考nginx.conf)
- 实现两路分流
- 本机
- 直播云(例:阿里云、腾讯云、ucloud)
- 实现直播水印效果
- 水印图片存放位置(容器内):/opt/images/logo.png
💻 部署运行
服务器
- 安装docker(Centos7,其他系统请发挥你的搜索功能)
$ yum -y install docker #安装docker
$ systemctl enable docker #配置开机启动
$ systemctl start docker #启动docker服务
- 拉取docker镜像并运行
#如果速度慢可使用阿里云:docker pull registry.cn-shenzhen.aliyuncs.com/ar414/nginx-rtmp-ffmpeg:v1
$ docker pull ar414/nginx-rtmp-ffmpeg
$ docker run -it -d -p 1935:1935 -p 8080:80 --rm ar414/nginx-rtmp-ffmpeg
- 推流地址(Stream live content to):
rtmp://:1935/stream/$STREAM_NAME
- SSL证书
#/etc/nginx/nginx.conf
listen 443 ssl;
ssl_certificate /opt/certs/example.com.crt;
ssl_certificate_key /opt/certs/example.com.key;
OBS配置
观看测试
HLS播放测试工具:http://player.alicdn.com/aliplayer/setting/setting.html (如果配置了证书则使用https)
- HLS播放地址
- 有水印:http://
:8080/hls/ar414_wm.m3u8 - 无水印:http://
:8080/hls/ar414.m3u8
- 有水印:http://
RTMP测试工具:PotPlayer
- RTMP播放地址
- 无水印:rtmp://
:1935/stream/ar414 - 有水印:需要分流到其他服务器上
- 无水印:rtmp://
📄 配置文件简解(分流、水印及水印位置)
- RTMP配置
rtmp {
server {
listen 1935; #端口
chunk_size 4000;
#RTMP 直播流配置
application stream {
live on;
#添加水印及分流,这次方便测试直接分流到当前服务器hls
#实际生产一般都分流到直播云(腾讯云、阿里云、ucloud)
#只需把需要分流的地址替换即可
#有水印:rtmp://localhost:1935/hls/$name_wm
#无水印:rtmp://localhost:1935/hls/$name
exec ffmpeg -i rtmp://localhost:1935/stream/$name -i /opt/images/ar414.png
-filter_complex "overlay=10:10,split=1[ar414]"
-map '[ar414]' -map 0:a -s 1920x1080 -c:v libx264 -c:a aac -g 30 -r 30 -tune zerolatency -preset veryfast -crf 23 -f flv rtmp://localhost:1935/hls/$name_wm
-c:a libfdk_aac -b:a 128k -c:v libx264 -b:v 2500k -f flv -g 30 -r 30 -s 1920x1080 -preset superfast -profile:v baseline rtmp://localhost:1935/hls/$name;
}
application hls {
live on;
hls on;
hls_fragment 5;
hls_path /opt/data/hls;
}
}
}
- 如果需要推多个直播云则复制多个 exec ffmpeg即可 如下:
application stream {
live on;
#分流至本机hls
exec ffmpeg -i rtmp://localhost:1935/stream/$name -i /opt/images/ar414.png
-filter_complex "overlay=10:10,split=1[ar414]"
-map '[ar414]' -map 0:a -s 1920x1080 -c:v libx264 -c:a aac -g 30 -r 30 -tune zerolatency -preset veryfast -crf 23 -f flv rtmp://localhost:1935/hls/$name_wm
-c:a libfdk_aac -b:a 128k -c:v libx264 -b:v 2500k -f flv -g 30 -r 30 -s 1920x1080 -preset superfast -profile:v baseline rtmp://localhost:1935/hls/$name;
#分流至腾讯云
exec ffmpeg -i rtmp://localhost:1935/stream/$name -i /opt/images/ar414.png
-filter_complex "overlay=10:10,split=1[ar414]"
-map '[ar414]' -map 0:a -s 1920x1080 -c:v libx264 -c:a aac -g 30 -r 30 -tune zerolatency -preset veryfast -crf 23 -f flv rtmp://live-push.tencent.com/stream/$name_wm
-c:a libfdk_aac -b:a 128k -c:v libx264 -b:v 2500k -f flv -g 30 -r 30 -s 1920x1080 -preset superfast -profile:v baseline rtmp://live-push.tencent.com/stream/$name;
#分流至阿里云
exec ffmpeg -i rtmp://localhost:1935/stream/$name -i /opt/images/ar414.png
-filter_complex "overlay=10:10,split=1[ar414]"
-map '[ar414]' -map 0:a -s 1920x1080 -c:v libx264 -c:a aac -g 30 -r 30 -tune zerolatency -preset veryfast -crf 23 -f flv rtmp://live-push.aliyun.com/stream/$name_wm
-c:a libfdk_aac -b:a 128k -c:v libx264 -b:v 2500k -f flv -g 30 -r 30 -s 1920x1080 -preset superfast -profile:v baseline rtmp://live-push.aliyun.com/stream/$name;
}
- 水印位置
- 水印位置
水印图片位置 overlay值 左上角 10:10 右上角 main_w-overlay_w-10:10 左下角 10:main_h-overlay_h-10 右下角 main_w-overlay_w-10 : main_h-overlay_h-10 - overlay参数
参数 说明 main_w 视频单帧图像宽度(当前配置文件1920) main_h 视频单帧图像高度(当前配置文件1080) overlay_w 水印图片的宽度 overlay_h 水印图片的高度
from https://github.com/ar414-com/nginx-rtmp-ffmpeg-conf
深度解析RTMP直播协议:从保姆级入门到高级优化
涉及到RTMP直播协议,了解其工作原理和优化技巧对于提升直播质量和用户体验至关重要。在这篇详细剖析的文章中,我们将深入探讨RTMP直播协议的各个方面,以及实际开发中遇到的一些情况,并分享一些实用的优化方法,帮助你打造更出色的直播内容。
我们将介绍RTMP协议的基本原理和流程,包括建立连接、数据传输和结束会话等步骤。我们将深入了解RTMP协议的工作机制,包括信令传输、数据分块和流控制等关键概念,以帮助你更好地理解其内部运作。文章最后对 enhanced rtmp
进行了介绍, 推展了RTMP对H265, AV1视频编码的支持。
1. 简介
RTMP协议是Real Time Message Protocol
(实时信息传输协议)的缩写,它是由Adobe公司提出的一种应用层的协议,用来解决多媒体数据传输流的多路复用(Multiplexing)和分包(packetizing)的问题。随着VR技术的发展,视频直播等领域逐渐活跃起来,RTMP作为业内广泛使用的协议也重新被相关开发者重视起来。
RTMP协议基本特点
基于
TCP
协议的应用层协议默认通信端口
1935
RTMP URL格式
rtmp://ip:[port]/appName/streamName
例如: rtmp://192.168.178.218:1935/live/stream
详细参考:https://blog.csdn.net/ai2000ai/article/details/72771461
2. RTMP 握手
RTMP
握手分为简单握手和复杂握手,现在Adobe
公司使用RTMP
协议的产品用复杂握手的较多,不做介绍。
握手包格式
0 1 2 3 4 5 6 7
+-+-+-+-+-+-+-+-+
| version |
+-+-+-+-+-+-+-+-+
C0 and S0 bits
C0和S0:1
个字节,包含了RTMP版本, 当前RTMP协议的版本为 3
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| time (4 bytes) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| zero (4 bytes) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| random bytes |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| random bytes |
| (cont) |
| .... |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
C1 and S1 bits
C1和S1:4
字节时间戳,4
字节的0
,1528
字节的随机数
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| time (4 bytes) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| time2 (4 bytes) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| random echo |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| random echo |
| (cont) |
| .... |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
C2 and S2 bits
C2和S2:4
字节时间戳,4
字节从对端读到的时间戳,1528
字节随机数
RTMP握手基本过程
+-------------+ +-------------+
| Client | TCP/IP Network | Server |
+-------------+ | +-------------+
| | |
Uninitialized | Uninitialized
| C0 | |
|------------------->| C0 |
| |-------------------->|
| C1 | |
|------------------->| S0 |
| |<--------------------|
| | S1 |
Version sent |<--------------------|
| S0 | |
|<-------------------| |
| S1 | |
|<-------------------| Version sent
| | C1 |
| |-------------------->|
| C2 | |
|------------------->| S2 |
| |<--------------------|
Ack sent | Ack Sent
| S2 | |
|<-------------------| |
| | C2 |
| |-------------------->|
Handshake Done | Handshake Done
| | |
Pictorial Representation of Handshake
- 握手开始于客户端发送
C0
、C1
块。服务器收到C0
或C1
后发送S0
和S1
。 - 当客户端收齐
S0
和S1
后,开始发送C2
。当服务器收齐C0
和C1
后,开始发送S2
。 - 当客户端和服务器分别收到
S2
和C2
后,握手完成。
注意事项:
在实际工程应用中,一般是客户端先将
C0
,C1
块同时发出,服务器在收到C1
之后同时将S0
,S1
,S2
发给客户端。S2
的内容就是收到的C1
块的内容。之后客户端收到S1
块,并原样返回给服务器,简单握手完成。按照RTMP协议个要求,客户端需要校验C1
块的内容和S2
块的内容是否相同,相同的话才彻底完成握手过程,实际编写程序用一般都不去做校验。
RTMP握手的这个过程就是完成了两件事:
- 校验客户端和服务器端RTMP协议版本号
- 是发了一堆随机数据,校验网络状况。
3. RTMP 消息
RTMP消息格式:
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Message Type | Payload length |
| (1 byte) | (3 bytes) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Timestamp |
| (4 bytes) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Stream ID |
| (3 bytes) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Message Header
1字节消息类型
3字节负载消息长度
4字节时间戳
3字节
Stream ID
,区分消息流
注意事项:
实际RTMP通信中并未按照上述格式去发送RTMP消息,而是将RTMP 消息分块发送,之后将介绍RTMP消息分块。
RTMP 消息分块(chunking)
而对于基于TCP
的RTMP
协议而言,协议显得特别繁琐,但是有没有更好的替代方案。同时创建RTMP
消息分块是比较复杂的地方,涉及到了AFM
(也是Adobe
家的东西)格式数据的数据。
RTMP消息块格式
+--------------+----------------+--------------------+--------------+
| Basic Header | Message Header | Extended Timestamp | Chunk Data |
+--------------+----------------+--------------------+--------------+
| |
|<------------------- Chunk Header ----------------->|
Chunk Format
RTMP消息块构成:
- Basic Header
- Message Header
- Extended Timestamp
- Chunk Data
Chunk Basic header格式有3种: 格式1:
0 1 2 3 4 5 6 7
+-+-+-+-+-+-+-+-+
|fmt| cs id |
+-+-+-+-+-+-+-+-+
Chunk basic header 1
格式2:
0 1
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|fmt| 0 | cs id - 64 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Chunk basic header 2
格式3:
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|fmt| 1 | cs id - 64 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Chunk basic header 3
注意事项:
fmt: 用于指定
Chunk Header
里面Message Header
的类型,后面会介绍到 cs id: 是chunk stream id
的缩写,同一个RTMP
消息拆成的chunk
块拥有相同的cs id
, 用于区分chunk所属的RTMP消息,chunk basic header
的类型cs id
占用的字节数来确定
Message Header格式
Message Header的类型通过上文chunk basic header
中的fmt
指定,共4种:
格式0:
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| timestamp | message length|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| message length (cont) |message type id| msg stream id |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| message stream id (cont) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Chunk Message Header - Type 0
Message Header
占用11
个字节, 在chunk stream
的开始的第一个chunk
的时候必须采用这种格式。
timestamp:
3
个字节,因此它最多能表示到16777215=0xFFFFFF=2^24-1
, 当它的值超过这个最大值时,这三个字节都置为1,实际的timestamp
会转存到Extended Timestamp
字段中,接受端在判断timestamp
字段24个位都为1时就会去Extended timestamp
中解析实际的时间戳。message length:
3
个字节,表示实际发送的消息的数据如音频帧、视频帧等数据的长度,单位是字节。注意这里是Message的长度,也就是chunk属于的Message的总数据长度,而不是chunk本身Data的数据的长度。message type id:
1
个字节,表示实际发送的数据的类型,如8
代表音频数据、9
代表视频数据。msg stream id:4个字节,表示该chunk所在的流的
ID
,和Basic Header
的CSID
一样,它采用小端存储的方式
格式1:
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| timestamp | message length|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| message length (cont) |message type id|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Chunk Message Header - Type 1
Message Header
占用7个字节,省去了表示msg stream id
的4个字节,表示此chunk
和上一次发的chunk
所在的流相同。
- timestamp delta:3个字节,注意这里和格式0时不同,存储的是和上一个chunk的时间差。类似上面提到的
timestamp
,当它的值超过3个字节所能表示的最大值时,三个字节都置为1,实际的时间戳差值就会转存到Extended Timestamp
字段中,接受端在判断timestamp delta
字段24个位都为1时就会去Extended timestamp
中解析时机的与上次时间戳的差值。
格式2:
0 1 2
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| timestamp |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Chunk Message Header - Type 2
Message Header
占用3个字节,相对于格式1,又省去了表示消息长度的3个字节和表示消息类型的1个字节,表示此chunk和上一次发送的chunk所在的流、消息的长度和消息的类型都相同。余下的这三个字节表示timestamp delta
,使用同格式1。
格式3:
0字节,它表示这个chunk的Message Header
和上一个是完全相同的,无需再次传送
Extended Timestamp(扩展时间戳)
在chunk
中会有时间戳timestamp
和时间戳差timestamp delta
, 只有这两者之一大于3个字节能表示的最大数值0xFFFFFF=16777215
时,才会用这个字段来表示真正的时间戳,否则这个字段不传(感谢评论区 @hijiang指出错误)。
扩展时间戳占4
个字节,能表示的最大数值就是0xFFFFFFFF=4294967295
。当扩展时间戳启用时,timestamp
字段或者timestamp delta
要全置为1
,表示应该去扩展时间戳字段来提取真正的时间戳或者时间戳差。注意扩展时间戳存储的是完整值,而不是减去时间戳或者时间戳差的值。
Chunk Data(块数据): 用户层面上真正想要发送的与协议无关的数据,长度在(0,chunkSize]之间, chunk size
默认为128
字节。
RTMP 消息分块注意事项
- Chunk Size
RTMP是按照chunk size
进行分块,chunk size
指的是 chunk
的payload
部分的大小,不包括chunk basic header
和 chunk message header
长度。客户端和服务器端各自维护了两个chunk size
, 分别是自身分块的chunk size
和 对端 的chunk size
, 默认的这两个chunk size
都是128字节。通过向对端发送set chunk size
消息可以告知对方更改了 chunk size
的大小。
- Chunk Type
RTMP消息分成的Chunk
有4
种类型,可以通过 chunk basic header
的高两位(fmt
)指定,一般在拆包的时候会把一个RTMP消息拆成以格式0
开始的chunk
,之后的包拆成格式3
类型的chunk
,我查看了有不少代码也是这样实现的,这样也是最简单的实现。
如果第二个message
和第一个message
的message stream ID
相同,并且第二个message
的长度也大于了chunk size
,那么该如何拆包?当时查了很多资料,都没有介绍。后来看了一些源码,如 SRS
,FFMPEG
中的实现,发现第二个message
可以拆成Type_1
类型一个chunk
, message
剩余的部分拆成Type_3
类型的chunk
。FFMPEG
中就是这么做的。
RTMP 交互消息
推流RTMP消息交互流程:关于推流的过程,RTMP的协议文档上给了上图示例,说一下推流注意事项:
Connect 消息
RTMP 命令消息格式:
+----------------+---------+---------------------------------------+
| Field Name | Type | Description |
+--------------- +---------+---------------------------------------+
| Command Name | String | Name of the command. Set to "connect".|
+----------------+---------+---------------------------------------+
| Transaction ID | Number | Always set to 1. |
+----------------+---------+---------------------------------------+
| Command Object | Object | Command information object which has |
| | | the name-value pairs. |
+----------------+---------+---------------------------------------+
| Optional User | Object | Any optional information |
| Arguments | | |
+----------------+---------+---------------------------------------+
RTMP握手之后先发送一个connect
命令消息,命令里面包含什么东西,协议中没有具体规定,实际通信中要携带 rtmp url 中的 appName
字段,并且指定一些编解码的信息,并以AMF
格式发送, 下面是用wireshake抓取connect
命令需要包含的参数信息:
这些信息协议中并没有特别详细说明, 在librtmp
,srs-librtmp
这些源码中,以及用wireshark 抓包的时候可以看到。
服务器返回的是一个_result
命令类型消息,这个消息的payload length
一般不会大于128
字节,但是在最新的nginx-rtmp
中返回的消息长度会大于128字节。
消息的transactionID
是用来标识command
类型的消息的,服务器返回的_result
消息可以通过transactionID
来区分是对哪个命令的回应,connect
命令发完之后还要发送其他命令消息,要保证他们的transactionID
不相同。
发送完connect
命令之后一般会发一个 set chunk size
消息来设置chunk size
的大小,也可以不发。
Window Acknowledgement Size
是设置接收端消息窗口大小,一般是2500000
字节,即告诉对端在收到设置的窗口大小长度的数据之后要返回一个ACK
消息。在实际做推流的时候推流端要接收很少的服务器数据,远远到达不了窗口大小,所以这个消息可以不发。而对于服务器返回的ACK
消息一般也不做处理,默认服务器都已经收到了所有消息了。
之后要等待服务器对于connect
消息的回应的,一般是把服务器返回的chunk
都读完,组包成完整的RTMP
消息,没有错误就可以进行下一步了。
Create Stream 消息
创建完RTMP
连接之后就可以创建RTMP
流,客户端要想服务器发送一个releaseStream
命令消息,之后是FCPublish
命令消息,在之后是createStream
命令消息。
当发送完createStream
消息之后,解析服务器返回的消息会得到一个stream ID
。
这个ID也就是以后和服务器通信的 message stream ID
, 一般返回的是1,不固定。
Publish Stream
推流准备工作的最后一步是Publish Stream
,即向服务器发一个publish
命令消息,消息中会带有流名称字段,即rtmp url中的 streamName
,这个命令的message stream ID
就是上面 create stream
之后服务器返回的stream ID
,发完这个命令一般不用等待服务器返回的回应,直接发送音视频类型的RTMP数据包即可。有些rtmp库还会发setMetaData
消息,这个消息可以发也可以不发,里面包含了一些音视频meta data
的信息,如视频的分辨率等等。
整个推流过程rtmp 消息抓包
4. 推送音视频
当以上工作都完成的时候,就可以发送音视频了。音视频RTMP消息的Payload(消息体)中都放的是按照FLV-TAG
格式封的音视频包,具体可以参照FLV
封装的协议文档。格式必须封装正确,否则会造成播放端不能正常拿到音视频数据,无法播放音视频。
5. 关于RTMP的时间戳
RTMP的时间戳单位是毫秒ms
,在发送音视频之前一直为零,发送音视频消息包后时候必须保证时间戳是单调递增的, 按照视频帧间隔累计增加,时间戳必须打准确,否则播放端可能出现音视频不同步的情况。Srs-librtmp的源码中,如果推的是视频文件的话,发现他们是用H264的dts
作为时间戳的。实时音视频传输的时候是先获取了下某一时刻系统时间作为基准,然后每次相机采集到的视频包,与起始的基准时间相减,得到时间戳,这样可以保证时间戳的正确性。
6. 关于Chunk Stream ID
RTMP 的Chunk Steam ID
是用来区分某一个chunk是属于哪一个message
的 ,0和1是保留的。每次在发送一个不同类型的RTMP消息时都要有不用的chunk stream ID, 如上一个Message 是command类型的,之后要发送视频类型的消息,视频消息的chunk stream ID
要保证和上面 command
类型的消息不同。每一种消息类型的起始chunk 的类型必须是 Type_0
类型的,表明新的消息的起始。
7. Enhanced Rtmp
Enhanced Rtmp 是对RTMP协议的增强, 2023年03月开始支持 HEVC(H265), VP9, AV1等视频编码,具体参考 Enhanced Rtmp 项目。
OBS(29.1+版本)已经可以支持RTMP推流 H265, 去下载
FFmpeg已经计划支持 Enhanced Rtmp,参考国内ffmpeg 开发者刘歧的分享:FFmpeg直播和低延迟方向的进展
测试发现目前还没有播放器可以直接播放H265 RTMP流,测试的播放器包括:VLC,IINA,ffplay等, 可以自己编译 ffplay 来播放h265码流,参考施维大神的项目:ffmpeg_rtmp_h265, 以及这篇 编译ffplay使其支持播放rtmp h265
支持H265的RTMP服务器还是推荐国产, 杨成立大神的SRS项目,详细推流介绍:SRS HEVC
8. 协议总结
RTMP协议是个比较啰嗦的协议,实现起来也比较复杂,但通信过程过程相对简单。在直播的实际工程应用中,协议上很多地方都没有详细说明,注意了以上提到几点,基本能够保证RTMP音视频的通信正常。以上就是对RTMP协议的简介和一些注意事项,希望能帮到有需要的朋友,另外本文难免有错误或说的不够详细的地方,欢迎指正,一起交流探讨。
9. 参考项目
rtmp-publish-kit:纯Java重写了RTMP协议,做了个Android 推流项目,包含安卓相机采集,编码和RTMP推流
项目地址:https://github.com/pixpark/rtmp-publish-kit
No comments:
Post a Comment