基本RTSP服务器
对于一个基本的RTSP服务器而言,只需要定义好封装的包装格式,建立socket链接,定义RTSP询问、交互格式(SDP)即可。在socket链接部分,有两种方法,一种是基于TCP一种是基于UDP,在阻塞情况下,只需要在服务器端做轮询操作即可。实例如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
| WSADATA wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) { printf("PC Server Socket Start Up Error \n"); return -1; }
int serverSockfd;
serverSockfd = createTcpSocket(); if (serverSockfd < 0) { WSACleanup(); printf("failed to create tcp socket\n"); return -1; } if (bindSocketAddr(serverSockfd, "0.0.0.0", SERVER_PORT) < 0) { printf("failed to bind addr\n"); return -1; } if (listen(serverSockfd, 10) < 0) { printf("failed to listen\n"); return -1; }
printf("%s rtsp://127.0.0.1:%d\n",__FILE__, SERVER_PORT); while (true) { int clientSockfd; char clientIp[40]; int clientPort;
clientSockfd = acceptClient(serverSockfd, clientIp, &clientPort); if (clientSockfd < 0) { printf("failed to accept client\n"); return -1; }
printf("accept client;client ip:%s,client port:%d\n", clientIp, clientPort);
doClient(clientSockfd, clientIp, clientPort); } closesocket(serverSockfd); WSACleanup(); return 0;
|
在上面的基本RTSP服务器例子中,核心是doclient,也就是如何处理接收到的RTSP命令。核心是拆分行,并与协议做比对
这里不再赘述。
高性能RTSP服务器
何所谓“高性能”
- 在读取文件方面就需要定义一个线程池
- 实现快速重启,结束后快速释放端口
- 定义IO时间,创建只读的回调函数
- 阻塞很少,绝大部分都是回调函数
- 可以支持epoll poller select
- 设置了定时器,所有的调度都是基于定时器实现的。对于不同的线程,定时器都是独立的,需要设置一个定时器的定时器,保证同步,在WIN系统中,定时器回调由子线程托管,非win系统通过select网络模型。
main函数流程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| int main() {
srand(time(NULL));
EventScheduler* scheduler = EventScheduler::createNew(EventScheduler::POLLER_SELECT);
ThreadPool* threadPool = ThreadPool::createNew(1); MediaSessionManager* sessMgr = MediaSessionManager::createNew(); UsageEnvironment* env = UsageEnvironment::createNew(scheduler, threadPool); Ipv4Address rtspAddr("127.0.0.1", 8554); RtspServer* rtspServer = RtspServer::createNew(env, sessMgr, rtspAddr);
LOGI("----------session init start------"); { MediaSession* session = MediaSession::createNew("test"); MediaSource* source = H264FileMediaSource::createNew(env, "D:/code/C++/RTSP_server/data/video4.h264"); Sink* sink = H264FileSink::createNew(env, source); session->addSink(MediaSession::TrackId0, sink);
source = AACFileMeidaSource::createNew(env, "D:/code/C++/RTSP_server/data/video5.aac"); sink = AACFileSink::createNew(env, source); session->addSink(MediaSession::TrackId1, sink);
sessMgr->addSession(session); } LOGI("----------session init end------");
rtspServer->start();
env->scheduler()->loop(); return 0;
|
”流“部分模块模块
AAC音频与H264视频发送
在视频与音频的发送都定义成了两个部分,其一是source,其二是sink。前者用于将文件转换成码流,后者用于封装发送
在AAC结构体构造的时候就会指定打包好的环境,并且封装好handletask函数等待回调getFrameFromAACFile(uint8_t* buf, int size)与parseAdtsHeader(uint8_t* in, struct AdtsHeader* res)
sink过程中需要知道帧率,并按照帧率对音频进行发送。
session发送
session同样分成两个部分,一部分用于生成session,即将视频和音频组成一路track。另一部分是session的管理类。
在生成session部分,有组合流,生成交互SDP,生成RtpInstance,以及设置发送包的回调函数。并配置了多播地址的随机生成
管理session部分,分成添加session 移除session 和获得session
”事件管理“部分模块
事件类别
- IOEvent
- TriggerEvent
- TimerEvent
timermanager
select框架