在前面的文章中,我向大家分享了RTMP协议的交互过程,以及librtmp源代码的核心实现,今天我继续向大家讲解如何自己动手实现一个简单的rtmp服务。出于时间和精力以及水平的关系,可能本文代码的篇幅占比很大(代码中有实现逻辑的注解),而且内容组织并不好,敬请谅解。
编写rtmp服务需要哪些知识:
- 对RTMP协议有深入理解。
- 对网络编程有较丰富的经验。
- 对数据结构有较强的组织能力。
- 熟悉多线程开发。
第4条不是必须的,单线程也是能写的,并且可能会写得更好,只是需要有成熟的单线程开发框架。在单线程模式下,不能再使用套接口同步模型,因为很可能一个连接出现问题会把整个线程卡死,并且如果不使用协程的话,librtmp库通常也是不建议用了。
出于简单起见,本文示例的代码基于librtmp,除了基础的C++库和操作系统API,不使用其它任何框架,网络模型采用连接和线程1:1的方法,实现代码如下:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
编译服务前,需要先下载librtmp库并配置好环境,下载地址:
http://rtmpdump.mplayerhq.hu/download
完成librtmp库的安装配置后,继将将上述代码保存为testrtmp.cpp,然后编译:
g++ -o testrtmp testrtmp.cpp -std=c++11 -lrtmp -pthread
运行并开启VLC拉流,拉流地址:http://127.0.0.1:1936/live/a
[kdjie@localhost ~]$ ./testrtmp
connection:[1] coming...
connection:[1] read headerType:[0] packetType:[20] CSID:[3] StreamID:[0] hasAbsTimestamp:[1] nTimeStamp:[0] m_nBodySize:[201]
connection:[1] server invoking 1
connection:[1] connect, app:[live]
DEBUG: Invoking _result
connection:[1] read headerType:[0] packetType:[5] CSID:[2] StreamID:[0] hasAbsTimestamp:[1] nTimeStamp:[0] m_nBodySize:[4]
connection:[1] received: window ack size change to 5000000
connection:[1] read headerType:[1] packetType:[20] CSID:[3] StreamID:[0] hasAbsTimestamp:[0] nTimeStamp:[0] m_nBodySize:[25]
connection:[1] server invoking 2
connection:[1] createStream, streamID:[1]
DEBUG: Invoking _result
connection:[1] read headerType:[0] packetType:[20] CSID:[8] StreamID:[0] hasAbsTimestamp:[1] nTimeStamp:[0] m_nBodySize:[32]
connection:[1] server invoking 3
connection:[1] read headerType:[0] packetType:[20] CSID:[8] StreamID:[1] hasAbsTimestamp:[1] nTimeStamp:[0] m_nBodySize:[30]
connection:[1] server invoking 4
connection:[1] play, streamID:[1] playpath:[a] time:[-2000]
DEBUG: Invoking onStatus
connection:[1] now play...
启动推流:
VLC播放效果:
推流完成日志:
connection:[2] read headerType:[2] packetType:[8] CSID:[4] StreamID:[1] hasAbsTimestamp:[0] nTimeStamp:[24686] m_nBodySize:[210]
connection:[2] read headerType:[3] packetType:[8] CSID:[4] StreamID:[1] hasAbsTimestamp:[0] nTimeStamp:[24712] m_nBodySize:[210]
connection:[2] read headerType:[1] packetType:[9] CSID:[6] StreamID:[1] hasAbsTimestamp:[0] nTimeStamp:[24713] m_nBodySize:[9460]
connection:[2] read headerType:[3] packetType:[8] CSID:[4] StreamID:[1] hasAbsTimestamp:[0] nTimeStamp:[24738] m_nBodySize:[210]
connection:[2] read headerType:[3] packetType:[8] CSID:[4] StreamID:[1] hasAbsTimestamp:[0] nTimeStamp:[24764] m_nBodySize:[210]
connection:[2] read headerType:[1] packetType:[9] CSID:[6] StreamID:[1] hasAbsTimestamp:[0] nTimeStamp:[24775] m_nBodySize:[473]
connection:[2] read headerType:[3] packetType:[8] CSID:[4] StreamID:[1] hasAbsTimestamp:[0] nTimeStamp:[24790] m_nBodySize:[210]
connection:[2] read headerType:[1] packetType:[20] CSID:[3] StreamID:[0] hasAbsTimestamp:[0] nTimeStamp:[0] m_nBodySize:[28]
connection:[2] server invoking 6
connection:[2] FCUnpublish, playpath:[a]
connection:[2] read headerType:[1] packetType:[20] CSID:[3] StreamID:[0] hasAbsTimestamp:[0] nTimeStamp:[0] m_nBodySize:[34]
connection:[2] server invoking 7
connection:[2] deleteStream, streamID:[1]
ERROR: RTMP_ReadPacket, failed to read RTMP packet header
connection:[2] read error!
connection:[2] exit!
最后还需要说明的是,这个例子还有很多不完善的地方,所以请尽量别用于生产用途。