live555学习1

这几天在弄h264的rtsp 的服务端,开始是参考了网络上一个人的代码效发现效果不是太好。还是决定好好捋一下这个过程,为了怕以后忘了还是在这里记录一下。有关live555的更多内容请详细参考:http://blog.csdn.net/nkmnkm/article/category/1066093,本文有部分参考该博客。

首先创建

TaskScheduler *scheduler = BasicTaskScheduler::createNew();  
    _env = BasicUsageEnvironment::createNew(*scheduler);

这里就不用讲了,创建计划任务等会会执行 _env->taskScheduler().doEventLoop();

主要的工作如下:

1为所有需要操作的socket执行select。

2找出第一个应执行的socket任务(handler)并执行之。

3找到第一个应响应的事件,并执行之。

4找到第一个应执行的延迟任务并执行之。

具体过程可以参考一下live555的源码。

然后是创建一个RTSPServer

RTSPServer *rtspServer = RTSPServer::createNew(*_env, 8554);

可以看一下

RTSPServer::createNew(UsageEnvironment& env, Port ourPort,
     UserAuthenticationDatabase* authDatabase,
     unsigned reclamationTestSeconds) {
  int ourSocket = setUpOurSocket(env, ourPort); //建立TCP socket
  if (ourSocket == -1) return NULL;


  return new RTSPServer(env, ourSocket, ourPort, authDatabase, reclamationTestSeconds);
}

要监听客户端的连接,就需要利用任务调度机制,所以需要添加所以需添加一个socket handler,

祥见如下

RTSPServer::RTSPServer(UsageEnvironment& env,
      int ourSocket, Port ourPort,
      UserAuthenticationDatabase* authDatabase,
      unsigned reclamationTestSeconds)
  : Medium(env),
    fRTSPServerSocket(ourSocket), fRTSPServerPort(ourPort),
    fHTTPServerSocket(-1), fHTTPServerPort(0), fClientSessionsForHTTPTunneling(NULL),
    fAuthDB(authDatabase), fReclamationTestSeconds(reclamationTestSeconds),
    fServerMediaSessions(HashTable::create(STRING_HASH_KEYS)) {
#ifdef USE_SIGNALS
  // Ignore the SIGPIPE signal, so that clients on the same host that are killed
  // don't also kill us:
  signal(SIGPIPE, SIG_IGN);
#endif
  // Arrange to handle connections from others:
  env.taskScheduler().turnOnBackgroundReadHandling(fRTSPServerSocket,
  (TaskScheduler::BackgroundHandlerProc*)&incomingConnectionHandlerRTSP, this);
}

这里的turnOnBackgroundReadHandling是UsageEnvironment中的一个方法

void turnOnBackgroundReadHandling(int socketNum, BackgroundHandlerProc* handlerProc, void* clientData) {
    setBackgroundHandling(socketNum, SOCKET_READABLE, handlerProc, clientData);
  }

setBackgroundHandling在TaskScheduler类中的一个纯虚函数,但是他放在UsageEnvironment.hh中

这是一个添加socket任务的函数

BasicTaskScheduler中,他们的继承关系是:BasicTaskScheduler-》BasicTaskScheduler0-》TaskScheduler

void BasicTaskScheduler
  ::setBackgroundHandling(int socketNum, int conditionSet, BackgroundHandlerProc* handlerProc, void* clientData) {
  if (socketNum < 0) return;
  FD_CLR((unsigned)socketNum, &fReadSet);
  FD_CLR((unsigned)socketNum, &fWriteSet);
  FD_CLR((unsigned)socketNum, &fExceptionSet);
  if (conditionSet == 0) {
    fHandlers->clearHandler(socketNum);
    if (socketNum+1 == fMaxNumSockets) {
      --fMaxNumSockets;
    }
  } else {
    fHandlers->assignHandler(socketNum, conditionSet, handlerProc, clientData);
    if (socketNum+1 > fMaxNumSockets) {
      fMaxNumSockets = socketNum+1;
    }
    if (conditionSet&SOCKET_READABLE) FD_SET((unsigned)socketNum, &fReadSet);
    if (conditionSet&SOCKET_WRITABLE) FD_SET((unsigned)socketNum, &fWriteSet);
    if (conditionSet&SOCKET_EXCEPTION) FD_SET((unsigned)socketNum, &fExceptionSet);
  }
}

这里可以看到主要是为socketNum添加到select中set中

当收到客户的连接时需保存下代表客户端的新socket,以后用这个socket与这个客户通讯。每个客户将来会对应一个rtp会话,而且各客户的RTSP请求只控制自己的rtp会话,那么最好建立一个会话类,代表各客户的rtsp会话。可以看到在TaskScheduler处理sockect的函数为BackgroundHandlerProc

从上面的RTSPServer的代码中可以看到

env.taskScheduler().turnOnBackgroundReadHandling(fRTSPServerSocket,
   (TaskScheduler::BackgroundHandlerProc*)&incomingConnectionHandlerRTSP, this);
incomingConnectionHandlerRTSP为传入的他们的处理函数,这个函数如下:
void RTSPServer::incomingConnectionHandlerRTSP(void* instance, int /*mask*/) {
  RTSPServer* server = (RTSPServer*)instance;
  server->incomingConnectionHandlerRTSP1();
}
void RTSPServer::incomingConnectionHandlerRTSP1() {
  incomingConnectionHandler(fRTSPServerSocket);
}
void RTSPServer::incomingConnectionHandler(int serverSocket) {
  struct sockaddr_in clientAddr;
  SOCKLEN_T clientAddrLen = sizeof clientAddr;
//接受连接
  int clientSocket = accept(serverSocket, (struct sockaddr*)&clientAddr, &clientAddrLen);
  if (clientSocket < 0) {
    int err = envir().getErrno();
    if (err != EWOULDBLOCK) {
        envir().setResultErrMsg("accept() failed: ");
    }
    return;
  }
//设置socket的参数 
  makeSocketNonBlocking(clientSocket);
  increaseSendBufferTo(envir(), clientSocket, 50*1024);

#ifdef DEBUG
  envir() << "accept()ed connection from " << AddressString(clientAddr).val() << "\n";
#endif

 //产生一个sesson id  
  // Create a new object for this RTSP session.
  // (Choose a random 32-bit integer for the session id (it will be encoded as a 8-digit hex number).  We don't bother checking for
  //  a collision; the probability of two concurrent sessions getting the same session id is very low.)
  // (We do, however, avoid choosing session id 0, because that has a special use (by "OnDemandServerMediaSubsession").)
  unsigned sessionId;
  do { sessionId = (unsigned)our_random32(); } while (sessionId == 0);
//创建RTSPClientSession,注意传入的参数 
  (void)createNewClientSession(sessionId, clientSocket, clientAddr);
}
创建了一个RTSPClientSession
RTSPServer::createNewClientSession(unsigned sessionId, int clientSocket, struct sockaddr_in clientAddr) {
  return new RTSPClientSession(*this, sessionId, clientSocket, clientAddr);
}

RTSPClientSession要提供什么功能呢?可以想象:需要监听客户端的rtsp请求并回应它,需要在DESCRIBE请求中返回所请求的流的信息,需要在SETUP请求中建立起RTP会话,需要在TEARDOWN请求中关闭RTP会话,等等...

RTSPClientSession要侦听客户端的请求,就需把自己的socket handler加入计划任务。证据如下:

RTSPServer::RTSPClientSession
::RTSPClientSession(RTSPServer& ourServer, unsigned sessionId, int clientSocket, struct sockaddr_in clientAddr)
  : fOurServer(ourServer), fOurSessionId(sessionId),
    fOurServerMediaSession(NULL),
    fClientInputSocket(clientSocket), fClientOutputSocket(clientSocket), fClientAddr(clientAddr),
    fSessionCookie(NULL), fLivenessCheckTask(NULL),
    fIsMulticast(False), fSessionIsActive(True), fStreamAfterSETUP(False),
    fTCPStreamIdCount(0), fNumStreamStates(0), fStreamStates(NULL), fRecursionCount(0) {
  // Arrange to handle incoming requests:
  resetRequestBuffer();
  envir().taskScheduler().turnOnBackgroundReadHandling(fClientInputSocket,
     (TaskScheduler::BackgroundHandlerProc*)&incomingRequestHandler, this);
  noteLiveness();
}

执行函数是incomingRequestHandler,如下:

void RTSPServer::RTSPClientSession::incomingRequestHandler(void* instance, int /*mask*/) {
  RTSPClientSession* session = (RTSPClientSession*)instance;
  session->incomingRequestHandler1();
}
void RTSPServer::RTSPClientSession::incomingRequestHandler1() {
  struct sockaddr_in dummy; // 'from' address, meaningless in this case
  int bytesRead = readSocket(envir(), fClientInputSocket, &fRequestBuffer[fRequestBytesAlreadySeen], fRequestBufferBytesLeft, dummy);
  handleRequestBytes(bytesRead);
}

上面是read了一段数据,handleRequestBytes主要是一些数据的处理,由于代码太多,不一一列出,以下代码就是证据,主要是对一些rtsp的请求作出回应:

if (strcmp(cmdName, "OPTIONS") == 0) {
handleCmd_OPTIONS(cseq);
      } else if (strcmp(cmdName, "DESCRIBE") == 0) {
handleCmd_DESCRIBE(cseq, urlPreSuffix, urlSuffix, (char const*)fRequestBuffer);
      } else if (strcmp(cmdName, "SETUP") == 0) {
handleCmd_SETUP(cseq, urlPreSuffix, urlSuffix, (char const*)fRequestBuffer);
      } else if (strcmp(cmdName, "TEARDOWN") == 0
|| strcmp(cmdName, "PLAY") == 0
|| strcmp(cmdName, "PAUSE") == 0
|| strcmp(cmdName, "GET_PARAMETER") == 0
|| strcmp(cmdName, "SET_PARAMETER") == 0) {
handleCmd_withinSession(cmdName, urlPreSuffix, urlSuffix, cseq, (char const*)fRequestBuffer);
      } else {
handleCmd_notSupported(cseq);
      }

下面重点讲一下下RTSPClientSession响应DESCRIBE请求的过程:

void RTSPServer::RTSPClientSession::handleCmd_DESCRIBE(
char const* cseq,
char const* urlPreSuffix,
char const* urlSuffix,
char const* fullRequestStr)
{
char* sdpDescription = NULL;
char* rtspURL = NULL;
do {
//整理一下下RTSP地址
char urlTotalSuffix[RTSP_PARAM_STRING_MAX];
if (strlen(urlPreSuffix) + strlen(urlSuffix) + 2
> sizeof urlTotalSuffix) {
handleCmd_bad(cseq);
break;
}
urlTotalSuffix[0] = '\0';
if (urlPreSuffix[0] != '\0') {
strcat(urlTotalSuffix, urlPreSuffix);
strcat(urlTotalSuffix, "/");
}
strcat(urlTotalSuffix, urlSuffix);
//验证帐户和密码
if (!authenticationOK("DESCRIBE", cseq, urlTotalSuffix, fullRequestStr))
break;
// We should really check that the request contains an "Accept:" #####
// for "application/sdp", because that's what we're sending back #####
// Begin by looking up the "ServerMediaSession" object for the specified "urlTotalSuffix":
//跟据流的名字查找ServerMediaSession,如果找不到,会创建一个。每个ServerMediaSession中至少要包含一个
//ServerMediaSubsession。一个ServerMediaSession对应一个媒体,可以认为是Server上的一个文件,或一个实时获取设备。其包含的每个ServerMediaSubSession代表媒体中的一个Track。所以一个ServerMediaSession对应一个媒体,如果客户请求的媒体名相同,就使用已存在的ServerMediaSession,如果不同,就创建一个新的。一个流对应一个StreamState,StreamState与ServerMediaSubsession相关,但代表的是动态的,而ServerMediaSubsession代表静态的。
ServerMediaSession* session = fOurServer.lookupServerMediaSession(urlTotalSuffix);
if (session == NULL) {
handleCmd_notFound(cseq);
break;
}
// Then, assemble a SDP description for this session:
//获取SDP字符串,在函数内会依次获取每个ServerMediaSubSession的字符串然连接起来。
sdpDescription = session->generateSDPDescription();
if (sdpDescription == NULL) {
// This usually means that a file name that was specified for a
// "ServerMediaSubsession" does not exist.
snprintf((char*) fResponseBuffer, sizeof fResponseBuffer,
"RTSP/1.0 404 File Not Found, Or In Incorrect Format\r\n"
"CSeq: %s\r\n"
"%s\r\n", cseq, dateHeader());
break;
}
unsigned sdpDescriptionSize = strlen(sdpDescription);

// Also, generate our RTSP URL, for the "Content-Base:" header
// (which is necessary to ensure that the correct URL gets used in
// subsequent "SETUP" requests).
rtspURL = fOurServer.rtspURL(session, fClientInputSocket);

//形成响应DESCRIBE请求的RTSP字符串。
snprintf((char*) fResponseBuffer, sizeof fResponseBuffer,
"RTSP/1.0 200 OK\r\nCSeq: %s\r\n"
"%s"
"Content-Base: %s/\r\n"
"Content-Type: application/sdp\r\n"
"Content-Length: %d\r\n\r\n"
"%s", cseq, dateHeader(), rtspURL, sdpDescriptionSize,
sdpDescription);
} while (0);

delete[] sdpDescription;
delete[] rtspURL;

//返回后会被立即发送(没有把socket write操作放入计划任务中)。
}

这里我们稍微看下lookupServerMediaSession

ServerMediaSession* RTSPServer::lookupServerMediaSession(char const* streamName) {
  return (ServerMediaSession*)(fServerMediaSessions->Lookup(streamName));
}

fServerMediaSessions是HashTable类型,也就是一个HashTable的查找过程

一般RTSPServer建立后需要addServerMediaSession(ServerMediaSession*)

现在看一下这个addServerMediaSession

void RTSPServer::addServerMediaSession(ServerMediaSession* serverMediaSession) {
  if (serverMediaSession == NULL) return;
  char const* sessionName = serverMediaSession->streamName();
  if (sessionName == NULL) sessionName = "";
  ServerMediaSession* existingSession
    = (ServerMediaSession*)(fServerMediaSessions->Add(sessionName, (void*)serverMediaSession));
//fServerMediaSessions就是上文提到的HashTable类型指针。可以看出是一个sessionName对应一个serverMediaSession
  removeServerMediaSession(existingSession); // if any
}

这里需要理解serverMediaSession和ServerMediaSubsession,在上面的程序中已有解释,不过要好好理解。

这里的ServerMediaSession一般会新建一个,然后是addSubsession加入ServerMediaSubsession。

这个新建和添加的过程后面再来看,现在再回到请求响应的过程,rtsp的请求基本是

OPTION、DESCRIBE、SETUP、PLAY、TEARDOWN

讲过DESCRIBE,其他的就不一一列举,下面看看比较关键的PLAY。

在RTSPServer.cpp中

void RTSPServer::RTSPClientSession
  ::handleCmd_PLAY(ServerMediaSubsession* subsession, char const* cseq,
  char const* fullRequestStr)

这里的内容太多,基本都是回应的信息最后执行的是

 fStreamStates[i].subsession->startStream(fOurSessionId,
      fStreamStates[i].streamToken,
      (TaskFunc*)noteClientLiveness, this,
      rtpSeqNum, rtpTimestamp,
      handleAlternativeRequestByte, this);

那么我们来看一下这个startStream,但在ServerMediaSubsession中startStream为纯虚函数。

因此ServerMediaSession   addSubsession的时候应该是一个继承自ServerMediaSubsession的类,OnDemandServerMediaSubsession是继承自ServerMediaSubsession。

我们先看看OnDemandServerMediaSubsession中的startStream

void OnDemandServerMediaSubsession::statStream(unsigned clientSessionId,void* streamToken,TaskFunc* rtcpRRHandler,void* rtcpRRHandlerClientData,
unsigned short& rtpSeqNum,unsigned& rtpTimestamp,ServerRequestAlternativeByteHandler* serverRequestAlternativeByteHandler,void* serverRequestAlternativeByteHandlerClientData) {
  StreamState* streamState = (StreamState*)streamToken;
  Destinations* destinations
    = (Destinations*)(fDestinationsHashTable->Lookup((char const*)clientSessionId));
  if (streamState != NULL) {
    streamState->startPlaying(destinations,
         rtcpRRHandler, rtcpRRHandlerClientData,
         serverRequestAlternativeByteHandler, serverRequestAlternativeByteHandlerClientData);
    if (streamState->rtpSink() != NULL) {
      rtpSeqNum = streamState->rtpSink()->currentSeqNo();
      rtpTimestamp = streamState->rtpSink()->presetNextTimestamp();
    }
  }
}
最后调用的是 streamState->startPlaying,但是这个 streamState确实头一次见到,也没看到在哪里建立。

再想想哪里还没有看呢?记得中间setup命令的响应过程没看,现在就去看看void RTSPServer::RTSPClientSession::handleCmd_SETUP

这里代码挺多的,主要是构建回复命令,我们重点看

 

fStreamStates = new struct streamState[fNumStreamStates];
      iter.reset();
      ServerMediaSubsession* subsession;
      for (unsigned i = 0; i < fNumStreamStates; ++i) {
 subsession = iter.next();
 fStreamStates[i].subsession = subsession;
 fStreamStates[i].streamToken = NULL; // for now; it may be changed by the "getStreamParameters()" call that comes later
      }
    }

看到没有这里建立了streamState,但是 fStreamStates[i].streamToken赋的为空,源码的解释是// for now; it may be changed by the "getStreamParameters()" call that comes later
果然在下面我们看到了调用的代码。

subsession->getStreamParameters(fOurSessionId, fClientAddr.sin_addr.s_addr,
        clientRTPPort, clientRTCPPort,
        tcpSocketNum, rtpChannelId, rtcpChannelId,
        destinationAddress, destinationTTL, fIsMulticast,
        serverRTPPort, serverRTCPPort,
        fStreamStates[streamNum].streamToken);
在ServerMediaSubsession中getStreamParameters也是纯虚函数,我们来看看OnDemandServerMediaSubsession中的getStreamParameters。
if (fLastStreamToken != NULL && fReuseFirstSource) {
......
}else{
。。。。。。
FramedSource* mediaSource
      = createNewStreamSource(clientSessionId, streamBitrate);
...........
RTPSink* rtpSink;
..........
rtpSink = createNewRTPSink(rtpGroupsock, rtpPayloadType, mediaSource);

}

这两个比较重要创建rtspSink和FramedSource。

我们要实现自己的实时的h264 rtp传输,因此这个rtspSink和FramedSource要自己实现。因此我们addSubsession的时候要add一个继承自OnDemandServerMediaSubsession自己显示的类,主要目的是实现自己的rtspSink和FramedSource。从中我们还看到是建立了一个StreamState

证据如下:

streamToken = fLastStreamToken
     	= new StreamState(*this, serverRTPPort, serverRTCPPort, rtpSink, udpSink,
			streamBitrate, mediaSource,
			rtpGroupsock, rtcpGroupsock);

这里的rtpSink即为我们在上面createNewRTPSink出来的,这里的mediaSource 是通过FramedSource* mediaSource = createNewStreamSource(clientSessionId, streamBitrate);出来的。

StreamState::StreamState(OnDemandServerMediaSubsession& master,
                         Port const& serverRTPPort, Port const& serverRTCPPort,
			 RTPSink* rtpSink, BasicUDPSink* udpSink,
			 unsigned totalBW, FramedSource* mediaSource,
			 Groupsock* rtpGS, Groupsock* rtcpGS)
  : fMaster(master), fAreCurrentlyPlaying(False), fReferenceCount(1),
    fServerRTPPort(serverRTPPort), fServerRTCPPort(serverRTCPPort),
    fRTPSink(rtpSink), fUDPSink(udpSink), fStreamDuration(master.duration()),
    fTotalBW(totalBW), fRTCPInstance(NULL) /* created later */,
    fMediaSource(mediaSource), fRTPgs(rtpGS), fRTCPgs(rtcpGS) {
}

上面是StreamState的构造函数,fMediaSource即使我们自定义的reateNewStreamSource。

再回到streamState->startPlaying。

这部分的代码前面的是rtcp的部分,这里我们不做讨论,后面再做讨论。最后是执行了这里,这里的fMediaSource为new streamState时传入的mediaSource即为createNewStreamSource的返回我们这里为H264VideoStreamFramer1,其实是继承自

H264VideoStreamFramer的一个自己写的类。

f (fRTPSink != NULL) {
      fRTPSink->startPlaying(*fMediaSource, afterPlayingStreamState, this);
      fAreCurrentlyPlaying = True;
    }


而这里的继承关系如下:H264VideoRTPSink->VideoRTPSink->MultiFramedRTPSink->RTPSink->MediaSink

这里startPlaying是在MediaSink中实现的

Boolean MediaSink::startPlaying(MediaSource& source,
				afterPlayingFunc* afterFunc,
				void* afterClientData) {
  // Make sure we're not already being played:
  if (fSource != NULL) {
    envir().setResultMsg("This sink is already being played");
    return False;
  }

  // Make sure our source is compatible:
  if (!sourceIsCompatibleWithUs(source)) {
    envir().setResultMsg("MediaSink::startPlaying(): source is not compatible!");
    return False;
  }
  fSource = (FramedSource*)&source;

  fAfterFunc = afterFunc;
  fAfterClientData = afterClientData;
  return continuePlaying();
}


可以看到这里fSource即我们自己实现的那个source。fAfterFunC为afterPlayingStreamState。fAfterClientData为StreamState指针。

 

continuePlaying()在MediaSink中是一个纯虚函数,在H264VideoRTPSink中有实现

Boolean H264VideoRTPSink::continuePlaying() {
  // First, check whether we have a 'fragmenter' class set up yet.
  // If not, create it now:
  if (fOurFragmenter == NULL) {
    fOurFragmenter = new H264FUAFragmenter(envir(), fSource, OutPacketBuffer::maxSize,
					   ourMaxPacketSize() - 12/*RTP hdr size*/);
  } else {
    fOurFragmenter->reassignInputSource(fSource);
  }
  fSource = fOurFragmenter;

  // Then call the parent class's implementation:
  return MultiFramedRTPSink::continuePlaying();
}

这里要注意的是对fSource进行了复制。  fSource被指向了H264FUAFragmenter类,这个类主要实现了H264按照RFC3984进行RTP分包,不过这里的实现每个RTP中最多只包含一个NALU,没有实现组合封包的情形。

H264FUAFragmenter->FramedFilter->FramedSource。很明显,这是一个filter,包装了MPEGVideoStreamFramer类的对像。

以后的部分详见学习笔记2.

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值