前言
本文主要介绍自研的RtmpStreamSender.dll,rtmp库提供接口接收裸流数据,支持将裸流数据封装为flv格式并通过rtmp协议发流。
关于rtmp协议基础介绍可查看:https://blog.csdn.net/www_dong/article/details/131026072
关于rtmp收流介绍可查看:https://blog.csdn.net/www_dong/article/details/135073488
设计
主要流程:
- rtmp服务端启动tcp(端口:1935)监听,创建rtmp server对象;
- rtmp客户端发起tcp连接,连接成功后等到收流;
- rtmp服务端以动态库的形式呈现,提供SendData接口,有上层应用调用时塞入数据;
- rtmp服务端接收数据,将数据封装为flv格式;
- rtmp服务端将flv格式的流发送给rtmp客户端;
- rtmp客户端将数据解复用、解码和播放;
流程
- 创建tcp、启动端口监听;
c++
int RtmpServerThread(void* param)
{
assert(param);
CRtmpStreamSender* receiver = (CRtmpStreamSender*)param;
receiver->RtmpServerWorker();
return 0;
}
int CRtmpStreamSender::InitRtmpSession_()
{
if (0 != ParseUrl_())
{
printf("parse url failed\n");
return -1;
}
do
{
m_tcpServer = std::make_shared<ZDTcpServer>(nullptr, this);
if (!m_tcpServer.get()
|| 0 != m_tcpServer->TcpCreate()
|| 0 != m_tcpServer->TcpBind(m_rtmpPort)
|| 0 != m_tcpServer->TcpListen(5))
{
break;
}
m_command = std::make_shared<CRtmpServerCommand>(m_tcpServer);
if (!m_command.get()
|| 0 != m_command->Create())
{
break;
}
m_thread = std::thread(RtmpServerThread, this);
return 0;
} while (0);
Stop();
return 0;
}
// 在线程中等待连接
void CRtmpStreamSender::RtmpServerWorker()
{
bool bAccept = false;
while (m_running)
{
if (!bAccept)
{
if (0 == m_tcpServer->TcpAccept())
{
bAccept = true;
}
continue;
}
std::this_thread::sleep_for(std::chrono::seconds(5));
}
}
- 创建rtmp server
c++
// CRtmpServerCommand:librtmp封装类
m_command = std::make_shared<CRtmpServerCommand>(m_tcpServer);
if (!m_command.get()
|| 0 != m_command->Create())
{
break;
}
// 创建rtmp server
int CRtmpServerCommand::Create()
{
// 创建flv复用对象
m_flvMuxer = flv_muxer_create(SendRtmpDataMutexFLV, this);
if (!m_flvMuxer)
return -1;
struct rtmp_server_handler_t handler;
memset(&handler, 0, sizeof(handler));
handler.send = SendCallback;
// 创建rtmp server
m_rtmp = rtmp_server_create(this, &handler);
if (!m_rtmp)
return -1;
return 0;
}
- 数据封装
c++
// 上层通过SendData塞数据
int CRtmpStreamSender::SendData(void* data, int len, char* codec)
{
if (!m_command.get())
return -1;
return m_command->InputData(data, len, codec);
}
// 数据做flv封装
int CRtmpServerCommand::InputData(void* data, int len, const std::string& codec)
{
if (!m_flvMuxer || !data || len <= 0 || codec.empty())
return -1;
int ret = -1;
if (0 == codec.compare("H264"))
{
ret = flv_muxer_avc(m_flvMuxer, data, len, 0, 0);
}
else if (0 == codec.compare("H265"))
{
ret = flv_muxer_hevc(m_flvMuxer, data, len, 0, 0);
}
else if (0 == codec.compare("AAC"))
{
ret = flv_muxer_aac(m_flvMuxer, data, len, 0, 0);
}
return 0;
}
- 发送数据
c++
int CRtmpServerCommand::Package(int type, const void* data, size_t bytes, uint32_t timestamp)
{
if (!m_rtmp || !data || bytes <= 0)
return -1;
int ret = -1;
if (FLV_TYPE_AUDIO == type)
{
ret = rtmp_server_send_audio(m_rtmp, data, bytes, timestamp);
}
else if (FLV_TYPE_VIDEO == type)
{
ret = rtmp_server_send_video(m_rtmp, data, bytes, timestamp);
}
else if (FLV_TYPE_SCRIPT == type)
{
ret = rtmp_server_send_script(m_rtmp, data, bytes, timestamp);
}
if (0 != ret)
return ret;
return 0;
}
- 资源销毁
c++
void CRtmpServerCommand::Destroy()
{
if (m_rtmp)
{
rtmp_server_destroy(m_rtmp);
m_rtmp = nullptr;
}
if (m_flvMuxer)
{
flv_muxer_destroy(m_flvMuxer);
m_flvMuxer = nullptr;
}
}