使用live555作为服务端推流,基于udp传输时比较正常,但切换tcp 经常一段时间后自己断开。
原因一: 服务端故意为之。
rtsp 协议的传输使用tcp,在RTSPserver.hh, RTSPServer的构造函数中有:
class RTSPServer: public GenericMediaServer {
public:
static RTSPServer* createNew(UsageEnvironment& env, Port ourPort = 554,
UserAuthenticationDatabase* authDatabase = NULL,
unsigned reclamationSeconds = 65);
// If ourPort.num() == 0, we'll choose the port number
// Note: The caller is responsible for reclaiming "authDatabase"
// If "reclamationSeconds" > 0, then the "RTSPClientSession" state for
// each client will get reclaimed (and the corresponding RTP stream(s)
// torn down) if no RTSP commands - or RTCP "RR" packets - from the
// client are received in at least "reclamationSeconds" seconds.
......
}
第四个参数默认值是65, 表示如果 rtsp的socket 持续65s没有输入发生,就会关掉这个 tcp socket。对应代码在GenericMediaServer.cpp GenericMediaServer::ClientSession::noteLiveness(), 以节约资源。
同样的操作,在vlc作为rtsp服务端的时候,也有这样的机制,对应的代码在 /modules/stream_out/rtsp.c RtspSetup()
if (rtsp->timeout > 0)
if (vlc_timer_create(&rtsp->timer, RtspTimeOut, rtsp))
对于使用UDP传输的 rtp,传输数据是单独的udp socket,而使用rtp-over-tcp传输的时候,rtsp、rtp、rtcp、都是复用这一个tcp socket,所以这里关掉这个唯一的 tcp socket ,整个流传输都将关闭。
解决: 1.0 客户端按照一定频率发送 rtcp,类似保活功能
2.0 出于调试的目的,最简单的方法,就是关掉服务端这个超时关闭的功能。
// 第四个参数 0 ,表示一直保持连接,否则默认持续 65s 都没有rtsp的通信,就关掉rtsp socket.
RTSPServer* rtspServer = RTSPServer::createNew(*env, 8554, authDB,0);
原因二:服务端传输 rtp,写socket错误,主动关闭。
对于比较大的视频码流,或者网络很差的情况下,出现概率比较高。
对应代码在 RTPInterface.cpp
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;
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 /%d (blocking)\n", numBytesRemainingToSend,dataSize); fflush(stderr);
#endif
//采用阻塞方式,超时时间默认 500ms,重发
makeSocketBlocking(socketNum, RTPINTERFACE_BLOCKING_WRITE_TIMEOUT_MS);
sendResult = send(socketNum, (char const*)(&data[numBytesSentSoFar]), numBytesRemainingToSend, 0/*flags*/);
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
//重发失败,关闭socket
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);
}
}
视频帧比较大的时候,经常出现重发,然后重发又失败,导致主动关闭了socket. 在编译的时候加上 宏定义 -DDEBUG_SEND,可以看到这里的输出。
解决: 源码里面直接把这个 removeStreamSocket() 注释掉吧,虽然不建议直接修改源码。