live555的rtsp rtp over tcp 出流花屏问题
windows下用vlc播放 live555出的流会花屏,发现是发送缓冲区满了,live server会丢包
Boolean RTPInterface::sendRTPorRTCPPacketOverTCP(u_int8_t* packet, unsigned packetSize,
int socketNum, unsigned char streamChannelId) {
#ifdef DEBUG_SEND
fprintf(stderr, "sendRTPorRTCPPacketOverTCP: %d bytes over channel %d (socket %d)\n",
packetSize, streamChannelId, socketNum); fflush(stderr);
#endif
// Send a RTP/RTCP packet over TCP, using the encoding defined in RFC 2326, section 10.12:
// $<streamChannelId><packetSize><packet>
// (If the initial "send()" of '$<streamChannelId><packetSize>' succeeds, then we force
// the subsequent "send()" for the <packet> data to succeed, even if we have to do so with
// a blocking "send()".)
do {
u_int8_t framingHeader[4];
framingHeader[0] = '$';
framingHeader[1] = streamChannelId;
framingHeader[2] = (u_int8_t) ((packetSize&0xFF00)>>8);
framingHeader[3] = (u_int8_t) (packetSize&0xFF);
if (!sendDataOverTCP(socketNum, framingHeader, 4, False)) break;
if (!sendDataOverTCP(socketNum, packet, packetSize, True)) break;
#ifdef DEBUG_SEND
fprintf(stderr, "sendRTPorRTCPPacketOverTCP: completed\n"); fflush(stderr);
#endif
return True;
} while (0);
#ifdef DEBUG_SEND
fprintf(stderr, "sendRTPorRTCPPacketOverTCP: failed! (errno %d)\n", envir().getErrno()); fflush(stderr);
#endif
return False;
}
从上面的代码如果第一包发送失败就不会发第二包了,于是vlc的画面就会花掉,这里我们知道live555是非阻塞的
那为什么发送端的发送缓冲区会满呢,抓包发现是windows下的tcp延迟确认机制导致的,
windows下tcp延迟确认机制是默认开启的,这里发送出去的数据一直得不到确认,占着缓冲区,把缓冲区耗尽了。
快速的解决的方法也算简单吧,把第一个send的fasle改为true,那么当异步发送失败时就会变为阻塞发送了!
这样做也不算好吧,因为会阻塞。如果要实现不丢包则需要缓存未发送的数据,还比较麻烦,先就这样改吧,毕竟我已经把live555改为多线程的了:),即时阻塞住也不会影响其他线程的发送哦
下面的函数可以看到在makeSocketBlocking处临时的把发送转为阻塞发送了哈
Boolean RTPInterface::sendDataOverTCP(int socketNum, u_int8_t const* data, unsigned dataSize, Boolean forceSendToSucceed) {
int sendResult = send(socketNum, (char const*)data, dataSize, 0/*flags*/);
if (sendResult < (int)dataSize)
{
// The TCP send() failed - at least partially.
unsigned numBytesSentSoFar = sendResult < 0 ? 0 : (unsigned)sendResult;
fprintf(stderr, "sendResult %d < dataSize %d, forceSendToSucceed %d, getErrno %d\n"
, sendResult, dataSize, forceSendToSucceed, envir().getErrno());
if (numBytesSentSoFar > 0 || (forceSendToSucceed && envir().getErrno() == EAGAIN))
{
// The OS's TCP send buffer has filled up (because the stream's bitrate has exceeded
// the capacity of the TCP connection!).
// Force this data write to succeed, by blocking if necessary until it does:
unsigned numBytesRemainingToSend = dataSize - numBytesSentSoFar;
#ifdef DEBUG_SEND
fprintf(stderr, "sendDataOverTCP: resending %d-byte send (blocking)\n", numBytesRemainingToSend); fflush(stderr);
#endif
makeSocketBlocking(socketNum, RTPINTERFACE_BLOCKING_WRITE_TIMEOUT_MS);
sendResult = send(socketNum, (char const*)(&data[numBytesSentSoFar]), numBytesRemainingToSend, 0/*flags*/);
fprintf(stderr, "block send %d\n", sendResult);
if ((unsigned)sendResult != numBytesRemainingToSend)
{
// The blocking "send()" failed, or timed out. In either case, we assume that the
// TCP connection has failed (or is 'hanging' indefinitely), and we stop using it
// (for both RTP and RTP).
// (If we kept using the socket here, the RTP or RTCP packet write would be in an
// incomplete, inconsistent state.)
#ifdef DEBUG_SEND
fprintf(stderr, "sendDataOverTCP: blocking send() failed (delivering %d bytes out of %d); closing socket %d\n", sendResult, numBytesRemainingToSend, socketNum); fflush(stderr);
#endif
removeStreamSocket(socketNum, 0xFF);
return False;
}
makeSocketNonBlocking(socketNum);
return True;
}
else if (sendResult < 0 && envir().getErrno() != EAGAIN)
{
// Because the "send()" call failed, assume that the socket is now unusable, so stop
// using it (for both RTP and RTCP):
removeStreamSocket(socketNum, 0xFF);
}
return False;
}
return True;
}