丹东网站建设公司,鹤壁建设企业网站公司,网上做计算机一级的网站是,wordpress 地理位置签到librtmp是一个RTMP的开源库#xff0c;很多地方用它来做推流、拉流。它是RTMPDump开源软件里的一部分#xff0c;librtmp的下载地址#xff1a;RTMPDump#xff0c;目前最新版是V2.3。本文重点介绍librtmp优化。
1、调整网络输出块大小。
RTMP_Connect0函数中LibRTMP是关… librtmp是一个RTMP的开源库很多地方用它来做推流、拉流。它是RTMPDump开源软件里的一部分librtmp的下载地址RTMPDump目前最新版是V2.3。本文重点介绍librtmp优化。
1、调整网络输出块大小。
RTMP_Connect0函数中LibRTMP是关闭了Nagle算法这个TCP选项的为了实时性这样做是好的但是要注意到LibRTMP的结构体RTMP的成员是有m_outChunkSize并且在RTMP_Init函数中被初始化了默认值128然后整个LibRTMP代码没有改变m_outChunkSize的接口函数内部也没有改变m_outChunkSize的实现逻辑也没有发送改变块大小的消息给流媒体服务器的代码逻辑关闭Nagle加如此小的块大小会导致很多小包而以太网的MTU是1500这样如果用在播放客户端由于主要是接收媒体流到也没有什么但是如果用在发布媒体流的推流客户端网络效率就太低了并且IP小包太多还会引起流媒体的服务器软中断升高导致内核占用的CPU过高。m_outChunkSize在发送给流媒体服务器消息会用于分块所以从这个方面来说LibRTMP还是部分支持改变块大小的这部分逻辑实现不需要任何改变。
调整输出块大小的函数
static int
ChangeChunkSize(RTMP *r,int outChunkSize)
{
RTMPPacket packet;
char pbuf[RTMP_MAX_HEADER_SIZE 4];packet.m_nBytesRead 0;
packet.m_body pbuf RTMP_MAX_HEADER_SIZE;packet.m_packetType RTMP_PACKET_TYPE_CHUNK_SIZE;
packet.m_nChannel 0x04;
packet.m_headerType RTMP_PACKET_SIZE_LARGE;
packet.m_nTimeStamp 0;
packet.m_nInfoField2 0;
packet.m_hasAbsTimestamp 0;
packet.m_nBodySize 4;
r-m_outChunkSize outChunkSize;r-m_outChunkSize htonl(r-m_outChunkSize);memcpy(packet.m_body, r-m_outChunkSize, 4);r-m_outChunkSize ntohl(r-m_outChunkSize);return RTMP_SendPacket(r, packet, TRUE);
}
注1RTMP协议的消息类型01RTMP_PACKET_TYPE_CHUNK_SIZE宏的值就是用于改变输出块大小的消息类型结合MTU
注2outChunkSize大小可以选择1500-20(IP头)-20TCP头1460考虑到IP头、TCP头有扩展选项加之PPPoE为保证起见可选为1360也可以设为大于MTU的其它值不过这样的话就会出现IP分片了也不是好习惯。
注3每当调用本函数后就顺便修改了RTMP的成员变量m_outChunkSize以保持与服务器收到的一致。
调整网络输出块大小的函数的时机
随时可以调整只不过在调用ChangeChunkSize函数后要注意到这个函数内部已经改变了RTMP的成员变量m_outChunkSize这样在调用这个函数之后的所有发给流媒体服务器的消息要以这个块大小来分块由于TCP的有序性服务器在收到该改变块大小的消息后也会以此块大小来解析后序的所有消息由于播放客户端主要是拉流播放端需要传给服务器的数据不多可以不修改基于此可以在收到connect的响应后的处理逻辑中调用ChangeChunkSize函数具体如下HandleInvoke函数中部分代码
if (r-Link.protocol RTMP_FEATURE_WRITE)
{
ChangeChunkSize(r, 1360);//若不改拉流时的输出块大小在这里调用ChangeChunkSize
SendReleaseStream(r);
SendFCPublish(r);
}
else
{
RTMP_SendServerBW(r);
RTMP_SendCtrl(r, 3, 0, 300);
}
2、AMF_GetProp函数bug
librtmp\amf.c文件里有个函数大概是1124行
AMFObjectProperty *
AMF_GetProp(AMFObject *obj, const AVal *name, int nIndex)
{if (nIndex 0){if (nIndex obj-o_num) //这里有问题return obj-o_props[nIndex];}else{int n;for (n 0; n obj-o_num; n){if (AVMATCH(obj-o_props[n].p_name, name))return obj-o_props[n];}}return (AMFObjectProperty *)AMFProp_Invalid;
}
3、librtmp发送阻塞
在librtmp的RTMP_Connect0( )中加入了send超时设置。
/* set timeout */
{struct timeval timeout;timeout.tv_sec 5;timeout.tv_usec 0;SET_RCVTIMEO(tv, r-Link.timeout);if (setsockopt(r-m_sb.sb_socket, SOL_SOCKET, SO_RCVTIMEO, (char *)tv, sizeof(tv))){RTMP_Log(RTMP_LOGERROR, %s, Setting socket timeout to %ds failed!, __FUNCTION__, r-Link.timeout);}if(setsockopt(r-m_sb.sb_socket, SOL_SOCKET, SO_SNDTIMEO, (char*)timeout, sizeof(timeout)) -1){RTMP_Log(RTMP_LOGERROR, %s, Setting socket timeout to %ds failed!, __FUNCTION__, timeout.tv_sec);}else{RTMP_Log(RTMP_LOGDEBUG, %s, Setting socket timeout to %ds success!, __FUNCTION__, timeout.tv_sec);}
}
4、librtmp 推流网络断开导致整个应用程序crash的错误崩溃堆栈出现在WriteN函数中。 解决方法修改rtmp.c文件中WriteN函数
static int
WriteN(RTMP *r, const char *buffer, int n)
{const char *ptr buffer;
#ifdef CRYPTOchar *encrypted 0;char buf[RTMP_BUFFER_CACHE_SIZE];if (r-Link.rc4keyOut){if (n sizeof(buf))encrypted (char *)malloc(n);elseencrypted (char *)buf;ptr encrypted;RC4_encrypt2(r-Link.rc4keyOut, n, buffer, ptr);}
#endifwhile (n 0){int nBytes;if (r-Link.protocol RTMP_FEATURE_HTTP)nBytes HTTP_Post(r, RTMPT_SEND, ptr, n);elsenBytes RTMPSockBuf_Send(r-m_sb, ptr, n);/*RTMP_Log(RTMP_LOGDEBUG, %s: %d\n, __FUNCTION__, nBytes); */if (nBytes 0){int sockerr GetSockError();RTMP_Log(RTMP_LOGERROR, %s, RTMP send error %d (%d bytes), __FUNCTION__,sockerr, n);if (sockerr EINTR !RTMP_ctrlC)continue;if(WSAECONNABORTED sockerr || WSAECONNRESET sockerr){//连接被对方断开RTMP_CloseNoSendAnyData(r);}else{RTMP_Close(r);}n 1;break;}if (nBytes 0)break;n - nBytes;ptr nBytes;}#ifdef CRYPTOif (encrypted encrypted ! buf)free(encrypted);
#endifreturn n 0;
}
//新增RTMP_CloseNoSendAnyData函数
void
RTMP_CloseNoSendAnyData(RTMP *r)
{int i;if (RTMP_IsConnected(r)){if (r-m_stream_id 0){if (r-m_clientID.av_val){HTTP_Post(r, RTMPT_CLOSE, , 1);free(r-m_clientID.av_val);r-m_clientID.av_val NULL;r-m_clientID.av_len 0;}RTMPSockBuf_Close(r-m_sb);}r-m_stream_id -1;r-m_sb.sb_socket -1;r-m_nBWCheckCounter 0;r-m_nBytesIn 0;r-m_nBytesInSent 0;if (r-m_read.flags RTMP_READ_HEADER) {free(r-m_read.buf);r-m_read.buf NULL;}r-m_read.dataType 0;r-m_read.flags 0;r-m_read.status 0;r-m_read.nResumeTS 0;r-m_read.nIgnoredFrameCounter 0;r-m_read.nIgnoredFlvFrameCounter 0;r-m_write.m_nBytesRead 0;RTMPPacket_Free(r-m_write);for (i 0; i RTMP_CHANNELS; i){if (r-m_vecChannelsIn[i]){RTMPPacket_Free(r-m_vecChannelsIn[i]);free(r-m_vecChannelsIn[i]);r-m_vecChannelsIn[i] NULL;}if (r-m_vecChannelsOut[i]){free(r-m_vecChannelsOut[i]);r-m_vecChannelsOut[i] NULL;}}AV_clear(r-m_methodCalls, r-m_numCalls);r-m_methodCalls NULL;r-m_numCalls 0;r-m_numInvokes 0;r-m_bPlaying FALSE;r-m_sb.sb_size 0;r-m_msgCounter 0;r-m_resplen 0;r-m_unackd 0;free(r-Link.playpath0.av_val);r-Link.playpath0.av_val NULL;if (r-Link.lFlags RTMP_LF_FTCU){free(r-Link.tcUrl.av_val);r-Link.tcUrl.av_val NULL;r-Link.lFlags ^ RTMP_LF_FTCU;}#ifdef CRYPTOif (r-Link.dh){MDH_free(r-Link.dh);r-Link.dh NULL;}if (r-Link.rc4keyIn){RC4_free(r-Link.rc4keyIn);r-Link.rc4keyIn NULL;}if (r-Link.rc4keyOut){RC4_free(r-Link.rc4keyOut);r-Link.rc4keyOut NULL;}
#endif
}