本文主要介绍rtmp协议收流流程,在linux上搭建rtmp服务器,通过自研的rtmp收流库发起取流请求,使用ffmpeg+qt实现视频流的解码与播放。
关于rtmp协议基础介绍可查看:https://blog.csdn.net/www_dong/article/details/131026072
# 下载地址,下载zip包即可
https://github.com/arut/nginx-rtmp-module
# 下载地址
http://nginx.org/en/download.html
# 步骤1
yum -y install gcc pcre pcre-devel zlib zlib-devel openssl openssl-devel
# 步骤2
yum -y install libxml2 libxml2-dev
yum -y install libxslt-devel
# 在/home/rtmp/nginx-1.24.0下执行如下命令
./configure --add-module=./nginx-rtmp-module-master --with-http_ssl_module --with-http_ssl_module --with-http_xslt_module --with-http_flv_module --with-debug --with-http_gzip_static_module
rtmp {
server {
listen 1935; #监听端口,若被占用,可以更改
chunk_size 4000; #上传flv文件块儿的大小
application live { #创建一个叫live的应用
live on; #开启live的应用
allow publish 192.168.191.100;#192.168.191.100为服务器ip
allow play all;
}
}
}
# 在/home/rtmp/nginx-1.24.0下执行如下命令
make
make install
# 测试配置文件
cd /usr/local/nginx
./sbin/nginx -t
# 有如下打印说明配置正常
[root@localhost nginx]# ./sbin/nginx -t
nginx: the configuration file /usr/local/nginx/conf/nginx.conf syntax is ok
nginx: configuration file /usr/local/nginx/conf/nginx.conf test is successful
# 启动nginx
./sbin/nginx
# 查看是否正常启动
ps -aux | grep nginx
ffmpeg -re -stream_loop -1 -i test.h264 -c copy -f flv rtmp://192.168.191.100:1935/live/testrtmp
以上步骤执行完即完成rtmp服务器搭建。
说明:客户端—自研的rtmp收流库,服务器—上述搭建的rtmp服务器
// rtmp://192.168.191.100:1935/live/testrtmp
// appName:"live"
// streamPath: "testrtmp"
// tcurl: "rtmp://192.168.191.100:1935/live"
int CRtmpTcpStreamReceiver::ParseUrl_(std::string& appName, std::string& streamPath, std::string& tcurl)
{
if (m_rtmpUrl.empty())
return -1;
std::string url = m_rtmpUrl;
std::string::size_type pos_0 = url.rfind("/");
if (std::string::npos == pos_0)
return -1;
// "testrtmp"
streamPath = url.substr(pos_0+1, url.length());
// "rtmp://192.168.191.100:1935/live"
tcurl = url.substr(0, pos_0);
std::string tmpUrl = tcurl;
std::string::size_type pos_1 = url.rfind("rtmp://");
if (std::string::npos == pos_1)
return -1;
// ippUrl: "192.168.191.100:1935/live"
std::string ippUrl = tmpUrl.substr(pos_1+7, tmpUrl.length());
std::string::size_type pos_2 = ippUrl.rfind("/");
if (std::string::npos == pos_2)
return -1;
// "live"
appName = ippUrl.substr(pos_2+1, ippUrl.length());
// "192.168.191.100:1935"
std::string ipp = ippUrl.substr(0, pos_2);
std::string::size_type pos_3 = ipp.rfind(":");
if (std::string::npos == pos_3)
return -1;
m_rtmpIP = ipp.substr(0, pos_3);
m_rtmpPort = atoi(ipp.substr(pos_3+1, ipp.length()).c_str());
return 0;
}
int CRtmpTcpStreamReceiver::InitRtmpSession_()
{
// rtmp url解析
std::string appName = "", streamPath = "", turl = "";
if (0 != ParseUrl_(appName, streamPath, turl))
{
return -1;
}
do
{
// tcp连接
m_tcpClient = std::make_shared(nullptr, this);
if (!m_tcpClient.get()
|| 0 != m_tcpClient->TcpCreate()
|| 0 != m_tcpClient->TcpConnect(m_rtmpIP.c_str(), m_rtmpPort)
|| 0 != m_tcpClient->TcpSetNoBlock(false)
|| 0 != m_tcpClient->TcpRecvTimeout(5))
{
break;
}
// CRtmpCommand为librtmp库接口封装类
m_command = std::make_shared(m_tcpClient, m_func, m_user);
if (!m_command.get())
{
break;
}
// 创建接收实例
if (0 != m_command->Create(appName, streamPath, turl)
|| 0 != m_command->Start(1))
{
break;
}
return 0;
} while (0);
UnInitRtmpSession_();
return -1;
}
void CRtmpTcpStreamReceiver::RtmpWorker()
{
std::shared_ptr dataPacket(new char[RTMP_STREAM_DATA_SIZE], std::default_delete());
memset(dataPacket.get(), 0x00, RTMP_STREAM_DATA_SIZE);
int recvLen = 0;
while (m_running)
{
// tcp数据接收
recvLen = m_tcpClient->TcpRecv(dataPacket.get(), RTMP_STREAM_DATA_SIZE);
if (recvLen <= 0)
{
continue;
}
// 塞数据
if (0 != m_command->InputData(dataPacket.get(), recvLen))
{
break;
}
memset(dataPacket.get(), 0x00, RTMP_STREAM_DATA_SIZE);
}
}