参考网上文章调试了一个例子,实现从摄像头IPC通过rtsp获取码流转发到EasyDarwin服务器功能
参考文章:https://blog.youkuaiyun.com/xiejiashu/article/details/34434669
使用testRTSPClient例子编译了一个rtspclient,从EasyDarwin服务器获取码流功能
记录下相关信息:
使用的live555版本是2015年的,因为最新的版本去掉了DarwinInjector.cpp,不支持对Darwin的支持,后面有必要在考虑整合到最新版本的live555中
下载了文章中的代码,把转发IPC的代码合并到自己的工程中,由于live555版本的区别,编译不过,用比较工具查看代码,然后把ProxyServerMediaSubsession类的声明从ProxyServerMediaSession.cpp移到了ProxyServerMediaSession.hh中,同时增加了两个函数,编译通过,增加的函数是:
char const* mediumName() const { return fClientMediaSubsession.mediumName(); }
unsigned char rtpPayloadFormat() const { return fClientMediaSubsession.rtpPayloadFormat(); }
修改缓冲大小:
main函数中需添加
OutPacketBuffer::maxSize = 800000;
创建live555基本环境,所有live555程序必须的:
scheduler = BasicTaskScheduler::createNew();
env = BasicUsageEnvironment::createNew(*scheduler);
用ProxyServerMediaSession类实现连接IPC码流功能
sms = ProxyServerMediaSession::createNew(*env, NULL, src);
此调用会创建跟IPC的rtps连接
类的继承关系:
ProxyServerMediaSession->ServerMediaSession->Medium
ProxyServerMediaSession的构造函数会调用defaultCreateNewProxyRTSPClientFunc创建ProxyRTSPClient,ProxyRTSPClient类继承关系:
ProxyRTSPClient->RTSPClient -> Mediu
ProxyServerMediaSubsession -> OnDemandServerMediaSubsession -> ServerMediaSubsession - >Medium
创建ProxyRTSPClient的时候实际调用到的是构造函数,之后调用ProxyRTSPClient::sendDESCRIBE,最终调用到RTSPClient::sendDescribeCommand,发送DESCRIBE命令。
DESCRIBE发送成功后调用,用sdp信息调顺序:
ProxyRTSPClient::continueAfterDESCRIBE
ProxyServerMediaSession::continueAfterDESCRIBE
MediaSession::createNew创建 MediaSession对象
MediaSession::initializeWithSDP用sdp初始化MediaSession对象
createNewMediaSubsession(),创建MediaSubsession对象,可能创建多个,如:有音频和视频,会创建两个,根据SDP信息初始化MediaSubsession对象相关信息,添加到MediaSession对象的链表fSubsessionsHead中
ProxyServerMediaSubsession,根据MediaSubsession创建ProxyServerMediaSubsession对象,添加到ServerMediaSession的链表中
创建source:
用ProxyServerMediaSubsession对象调用ProxyServerMediaSubsession::createNewStreamSource分别创建音视频source,是FramedSource类型对象
RTSPClient::sendSetupCommand发送SETUP命令
ProxyRTSPClient::continueAfterSETUP,SETUP发送成功后调用这个函数
RTSPClient::sendPlayCommand,发送PLAY命令,连接音视频流
创建sink:
创建两个Groupsock对象,分别用来转发视频,音频流
用ProxyServerMediaSubsession对象调用ProxyServerMediaSubsession::createNewRTPSink创建视频、音频sink,是RTPSink类型:
H264VideoRTPSink::createNew,区分视频类型创建H264sink
创建DarwinInjector对象,并将sink添加到DarwinInjector对象中
DarwinInjector::createNew,创建DarwinInjector对象
DarwinInjector::addStream,添加sink
DarwinInjector::setDestination,建立与服务推流连接
sink对象调用MediaSink::startPlaying开始转发
MediaSink::startPlaying
H264or5VideoRTPSink::continuePlaying
MultiFramedRTPSink::continuePlaying
H264or5Fragmenter::doGetNextFrame()
H264or5Fragmenter::afterGettingFrame1
H264or5Fragmenter::afterGettingFrame
continuePlaying和doGetNextFrame循环调用
Boolean DarwinInjector::setDestination函数修改,解决没有RTCP时候不能发送音频问题:
if (ss->rtcpInstance() != NULL) {
ss->rtcpInstance()->setStreamSocket(fRTSPClient->socketNum(),
streamChannelId++);
}
else
{
streamChannelId++; //add 解决无rtcp的时候音频问题
}
RTPSink数据发送调用过程:
MultiFramedRTPSink::continuePlaying
MultiFramedRTPSink::buildAndSendPacket
MultiFramedRTPSink::packFrame
MultiFramedRTPSink::afterGettingFrame
MultiFramedRTPSink::sendNext
MultiFramedRTPSink::buildAndSendPacket
MultiFramedRTPSink::packFrame
MultiFramedRTPSink::afterGettingFrame
MultiFramedRTPSink::afterGettingFrame1
MultiFramedRTPSink::sendPacketIfNecessary
RTPInterface::sendPacket
高码率视频数据传输的优化点
对高清高码率的视频画面,每一帧的视频数据就会比较大,这个数值往往会超出live555内部默认的内存处理大小,因为对于live555的优化,主要就是集中在内存缓冲大小的扩大,以及避免内存数据拷贝。以下为根据实际开发和测试所总结出来的有效的优化点:
1.扩展帧解析buffer大小,即BANK_SIZE,默认值为150k,根据传输的H264数据帧大小,至少设置为300k。否则超出大小,可能会被Live555抛弃。
2.增加OutPacketBuffer::maxSize大小,同样为了容纳超大帧数据,否则可能会导致数据丢失。
3.在RTPInterface中,增加socket发送缓冲区大小,即increaseSendBufferTo函数的参数值
4.对MultiFramedRTPSink::sendPacketIfNecessary中,可以直接调用sendNext尝试组建RTP报文发送数据包,这样修改的优点是已读取的数据会被尽快发送出去,不过也多占用一些线程时间。
5.对于应用程序将数据从自己的线程传递给Live555的时候,应该尽量减少内存拷贝,最好是通过内存池的形式,以避免拷贝内存阻塞Live555事件循环
经过以上修改,以及应用程序内部代码的优化,在实际应用中,已经实现了10Mbps高码率的1080p以上高分辨率高清视频的流畅直播。
3. 看不到直播视频,如何排查?
我们可以先配置easydarwin.xml文件中的
true
字段为true,然后重新启动EasyDarwin,请求EasyDarwin,如果一点报文都没有打印,那就是你访问的地址错了!如果报文有打印,那就可以具体看看返回的错误码是多少了,错误码对照表:
响应码 | 报文描述 | 定义 |
200 | Success OK | 成功创建 |
201 | Success Created | 成功创建 |
202 | Success Accepted | 已接受用于处理,但处理尚未完成 |
204 | Success No Content | 已接收请求,但不存在要回送的信息 |
206 | Success Partial Content | 已接收请求,但要回送的信息不完整 |
301 | Redirect Permanent Moved | 请求的数据具有新的位置且更改是永久的。 |
302 | Redirect Temp Moved | 请求的数据临时具有不同 URI |
303 | Redirect See Other | 可在另一 URI 下找到对请求的响应 |
305 | Use Proxy | 必须通过位置字段中提供的代理来访问请求的资源 |
400 | Client Bad Request | 请求中有语法问题,或不能满足请求 |
401 | Client Unauthorized | 未授权客户端访问数据 |
402 | Payment Required | 需要付款,表示计费系统已有效 |
403 | Client Forbidden | 禁止, 即使有授权也不需要访问 |
404 | Not Found | 服务器找不到给定的资源 |
405 | Method Not Allowed | 请求的方法不支持 |
407 | Proxy Authentication Required | 代理认证请求,客户机首先必须使用代理认证自身 |
408 | Request Timeout | 请求超时 |
409 | Conflict | 请求冲突 |
412 | Precondition Failed | 前提条件失败 |
415 | Unsupported Media Type | 服务器拒绝服务请求,因为不支持请求实体的格式 |
500 | Server Internal Error | 内部错误,因为意外情况,服务器不能完成请求 |
501 | Server Not Implemented | 未执行,服务器不支持请求 |
502 | Server Bad Gateway | 错误网关,服务器接收到来自上游服务器的无效响应 |
503 | Server Unavailable | 由于临时过载或无法获得服务护,服务器无法处理请求 |
505 | RTSP Version Not Supported | 不支持的RTSP版本 |