庖丁解牛-----Live555源码彻底解密(根据testOnDemandRTSPServer例子讲解)

testOnDemandRTSPServer.cpp 讲解

    

Main函数:

int main(intargc, char** argv) {

     // Begin by setting up our usage environment:

     TaskScheduler* scheduler = BasicTaskScheduler::createNew();

     env = BasicUsageEnvironment::createNew(*scheduler);

 

     UserAuthenticationDatabase*authDB = NULL;

#ifdef ACCESS_CONTROL

     // To implement client access control to the RTSP server, do the following:

     authDB = new UserAuthenticationDatabase;

     authDB->addUserRecord("username1", "password1"); // replace these with real strings

     // Repeat the above with each <username>, <password> that you wish to allow

     // access to the server.

#endif

 

     // Create the RTSP server:

     RTSPServer* rtspServer = RTSPServer::createNew(*env, 8554,authDB);

     if (rtspServer ==NULL) {

         *env << "Failed to create RTSP server: " << env->getResultMsg() <<"\n";

         exit(1);

     }

 

     char const* descriptionString

         = "Session streamed by \"testOnDemandRTSPServer\"";

 

     // Set up each of the possible streams that can be served by the

     // RTSP server.  Each such stream is implemented using a

     // "ServerMediaSession" object, plus one or more

     // "ServerMediaSubsession" objects for each audio/video substream.

 

     // A H.264 video elementary stream:

     {

         char const* streamName = "h264ESVideoTest";

         char const* inputFileName = "test.264";

         ServerMediaSession* sms

              = ServerMediaSession::createNew(*env,streamName, streamName,

              descriptionString);

         sms->addSubsession(H264VideoFileServerMediaSubsession

              ::createNew(*env,inputFileName, reuseFirstSource));

         rtspServer->addServerMediaSession(sms);

 

         announceStream(rtspServer,sms, streamName,inputFileName);

     }

     env->taskScheduler().doEventLoop();// does not return

 

     return 0; // only to prevent compiler warning

}

 

 

建立Rtsp服务端(RTSPServer):

RTSPServer*

RTSPServer::createNew(UsageEnvironment&env, Port ourPort,

               UserAuthenticationDatabase*authDatabase,

               unsigned reclamationTestSeconds) {

  int ourSocket = setUpOurSocket(env,ourPort);

  if (ourSocket == -1)return NULL;

 

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

}

 

#define LISTEN_BACKLOG_SIZE 20

//建立服务端socket

int RTSPServer::setUpOurSocket(UsageEnvironment&env, Port& ourPort) {

  int ourSocket = -1;

 

  do {

    // The following statement is enabled by default.

    // Don't disable it (by defining ALLOW_RTSP_SERVER_PORT_REUSE) unless you know what you're doing.

#ifndef ALLOW_RTSP_SERVER_PORT_REUSE

    NoReuse dummy(env); // Don't use this socket if there's already a local server using it

#endif

 

    ourSocket = setupStreamSocket(env, ourPort);

    if (ourSocket < 0)break;

 

    // Make sure we have a big send buffer:

    if (!increaseSendBufferTo(env,ourSocket, 50*1024)) break;

 

    // Allow multiple simultaneous connections:

    if (listen(ourSocket,LISTEN_BACKLOG_SIZE) < 0) {

      env.setResultErrMsg("listen() failed: ");

      break;

    }

 

    if (ourPort.num() == 0) {

      // bind() will have chosen a port for us; return it also:

      if (!getSourcePort(env,ourSocket, ourPort))break;

    }

 

    return ourSocket;

  } while (0);

 

  if (ourSocket != -1) ::closeSocket(ourSocket);

  return -1;

}

 

Create socket:

 

int setupStreamSocket(UsageEnvironment&env,

                      Port port, Boolean makeNonBlocking) {

  if (!initializeWinsockIfNecessary()) {

    socketErr(env,"Failed to initialize 'winsock': ");

    return -1;

  }

 

  int newSocket = createSocket(SOCK_STREAM);

  if (newSocket < 0) {

    socketErr(env,"unable to create stream socket: ");

    return newSocket;

  }

 

  int reuseFlag = groupsockPriv(env)->reuseFlag;

  reclaimGroupsockPriv(env);

  if (setsockopt(newSocket,SOL_SOCKET, SO_REUSEADDR,

          (const char*)&reuseFlag, sizeof reuseFlag) < 0) {

    socketErr(env,"setsockopt(SO_REUSEADDR) error: ");

    closeSocket(newSocket);

    return -1;

  }

 

  // SO_REUSEPORT doesn't really make sense for TCP sockets, so we

  // normally don't set them.  However, if you really want to do this

  // #define REUSE_FOR_TCP

#ifdef REUSE_FOR_TCP

#if defined(__WIN32__) || defined(_WIN32)

  // Windoze doesn't properly handle SO_REUSEPORT

#else

#ifdef SO_REUSEPORT

  if (setsockopt(newSocket, SOL_SOCKET, SO_REUSEPORT,

          (const char*)&reuseFlag, sizeof reuseFlag) < 0) {

    socketErr(env, "setsockopt(SO_REUSEPORT) error: ");

    closeSocket(newSocket);

    return -1;

  }

#endif

#endif

#endif

 

  // Note: Windoze requires binding, even if the port number is 0

#if defined(__WIN32__) ||defined(_WIN32)

#else

  if (port.num() != 0 || ReceivingInterfaceAddr != INADDR_ANY) {

#endif

    MAKE_SOCKADDR_IN(name,ReceivingInterfaceAddr, port.num());

    if (bind(newSocket, (structsockaddr*)&name,sizeof name) != 0) {

      char tmpBuffer[100];

      sprintf(tmpBuffer,"bind() error (port number: %d): ",

           ntohs(port.num()));

      socketErr(env,tmpBuffer);

      closeSocket(newSocket);

      return -1;

    }

#if defined(__WIN32__) ||defined(_WIN32)

#else

  }

#endif

 

  if (makeNonBlocking) {

    if (!makeSocketNonBlocking(newSocket)) {

      socketErr(env,"failed to make non-blocking: ");

      closeSocket(newSocket);

      return -1;

    }

  }

 

  return newSocket;

}

 

//Rtsp构造函数如下:

RTSPServer::RTSPServer(UsageEnvironment&env,

                int ourSocket, Port ourPort,

                UserAuthenticationDatabase*authDatabase,

                unsigned reclamationTestSeconds)

  : Medium(env),

    fRTSPServerPort(ourPort),fRTSPServerSocket(ourSocket),fHTTPServerSocket(-1), fHTTPServerPort(0),

    fServerMediaSessions(HashTable::create(STRING_HASH_KEYS)),

    fClientConnections(HashTable::create(ONE_WORD_HASH_KEYS)),

    fClientConnectionsForHTTPTunneling(NULL),// will get created if needed

    fClientSessions(HashTable::create(STRING_HASH_KEYS)),

    fPendingRegisterRequests(HashTable::create(ONE_WORD_HASH_KEYS)),

    fAuthDB(authDatabase),fReclamationTestSeconds(reclamationTestSeconds) {

  ignoreSigPipeOnSocket(ourSocket);// so that clients on the same host that are killed don't also kill us

 

  // Arrange to handle connections from others:

  env.taskScheduler().turnOnBackgroundReadHandling(fRTSPServerSocket,

                               (TaskScheduler::BackgroundHandlerProc*)&incomingConnectionHandlerRTSP,this);

}

 

turnOnBackgroundReadHandling函数已经废弃了,为了兼容前面的版本在用它,其实就是调用setBackgroundHandling; turnOnBackgroundReadHandling函数如下:

 

  void turnOnBackgroundReadHandling(int socketNum, BackgroundHandlerProc* handlerProc, void* clientData) {

    setBackgroundHandling(socketNum,SOCKET_READABLE, handlerProc,clientData);

  }

 

 

在incomingConnectionHandler函数中接受客户端的请求;

 

void RTSPServer::incomingConnectionHandler(intserverSocket) {

  struct sockaddr_in clientAddr;

  SOCKLEN_T clientAddrLen = sizeof clientAddr;

  int clientSocket = accept(serverSocket, (structsockaddr*)&clientAddr, &clientAddrLen);

  if (clientSocket < 0) {

    int err = envir().getErrno();

    if (err !=EWOULDBLOCK) {

        envir().setResultErrMsg("accept() failed: ");

    }

    return;

  }

 

// Accept后的socket clientSocket设置成非阻塞

  makeSocketNonBlocking(clientSocket);

//调整发送缓冲区的size

  increaseSendBufferTo(envir(),clientSocket, 50*1024);

 

#ifdef DEBUG

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

#endif

 

  // Create a new object for handling this RTSP connection:

  (void)createNewClientConnection(clientSocket,clientAddr);

}

 

createNewClientConnection函数如下:

 

 

RTSPServer::RTSPClientConnection*

RTSPServer::createNewClientConnection(intclientSocket, structsockaddr_in clientAddr) {

  return new RTSPClientConnection(*this,clientSocket, clientAddr);

}

 

RTSPClientConnection函数如下:

RTSPServer::RTSPClientConnection

::RTSPClientConnection(RTSPServer&ourServer, intclientSocket, structsockaddr_in clientAddr)

  : fOurServer(ourServer),fIsActive(True),

    fClientInputSocket(clientSocket),fClientOutputSocket(clientSocket),fClientAddr(clientAddr),

    fRecursionCount(0), fOurSessionCookie(NULL) {

  // Add ourself to our 'client connections' table: //将客户端socket放在表中,以便后面处理该socket的请求

  fOurServer.fClientConnections->Add((charconst*)this, this);

 

  // Arrange to handle incoming requests:

  resetRequestBuffer();

  envir().taskScheduler().setBackgroundHandling(fClientInputSocket,SOCKET_READABLE|SOCKET_EXCEPTION,

                            (TaskScheduler::BackgroundHandlerProc*)&incomingRequestHandler,this);

}

 

void RTSPServer::RTSPClientConnection::incomingRequestHandler(void*instance, int /*mask*/) {

  RTSPClientConnection* session = (RTSPClientConnection*)instance;

  session->incomingRequestHandler1();

}

//socket从客户端读数据

void RTSPServer::RTSPClientConnection::incomingRequestHandler1() {

  struct sockaddr_in dummy; // 'from' address, meaningless in this case

 

  int bytesRead = readSocket(envir(),fClientInputSocket, &fRequestBuffer[fRequestBytesAlreadySeen],fRequestBufferBytesLeft, dummy);

  handleRequestBytes(bytesRead);

}

handleRequestBytes如下:

主要解析客户端的Rtsp请求信息:

void RTSPServer::RTSPClientConnection::handleRequestBytes(intnewBytesRead) {

  int numBytesRemaining = 0;

  ++fRecursionCount;

 

  do {

    RTSPServer::RTSPClientSession*clientSession = NULL;

 

    if (newBytesRead < 0 || (unsigned)newBytesRead >=fRequestBufferBytesLeft) {

      // Either the client socket has died, or the request was too big for us.

      // Terminate this connection:

#ifdef DEBUG

      fprintf(stderr, "RTSPClientConnection[%p]::handleRequestBytes() read %d new bytes (of %d); terminating connection!\n", this, newBytesRead, fRequestBufferBytesLeft);

#endif

      fIsActive = False;

      break;

    }

   

    Boolean endOfMsg = False;

    unsigned char* ptr = &fRequestBuffer[fRequestBytesAlreadySeen];

#ifdef DEBUG

    ptr[newBytesRead] = '\0';

    fprintf(stderr, "RTSPClientConnection[%p]::handleRequestBytes() %s %d new bytes:%s\n",

         this, numBytesRemaining > 0 ? "processing" : "read", newBytesRead, ptr);

#endif

   

    if (fClientOutputSocket !=fClientInputSocket) {

      // We're doing RTSP-over-HTTP tunneling, and input commands are assumed to have been Base64-encoded.

      // We therefore Base64-decode as much of this new data as we can (i.e., up to a multiple of 4 bytes).

 

      // But first, we remove any whitespace that may be in the input data:

      unsigned toIndex = 0;

      for (intfromIndex = 0; fromIndex <newBytesRead; ++fromIndex) {

     char c = ptr[fromIndex];

     if (!(c ==' ' || c =='\t' || c =='\r' || c =='\n')) { // not 'whitespace': space,tab,CR,NL

       ptr[toIndex++] =c;

     }

      }

      newBytesRead = toIndex;

 

      unsigned numBytesToDecode = fBase64RemainderCount +newBytesRead;

      unsigned newBase64RemainderCount = numBytesToDecode%4;

      numBytesToDecode -= newBase64RemainderCount;

      if (numBytesToDecode > 0) {

     ptr[newBytesRead] ='\0';

     unsigned decodedSize;

     unsigned char* decodedBytes = base64Decode((char const*)(ptr-fBase64RemainderCount),numBytesToDecode, decodedSize);

#ifdef DEBUG

     fprintf(stderr, "Base64-decoded %d input bytes into %d new bytes:", numBytesToDecode, decodedSize);

     for (unsigned k = 0; k < decodedSize; ++k) fprintf(stderr, "%c", decodedBytes[k]);

     fprintf(stderr, "\n");

#endif

    

     // Copy the new decoded bytes in place of the old ones (we can do this because there are fewer decoded bytes than original):

     unsigned char* to = ptr-fBase64RemainderCount;

     for (unsignedi = 0; i < decodedSize; ++i) *to++ =decodedBytes[i];

    

     // Then copy any remaining (undecoded) bytes to the end:

     for (unsignedj = 0; j < newBase64RemainderCount; ++j) *to++ = (ptr-fBase64RemainderCount+numBytesToDecode)[j];

    

     newBytesRead = decodedSize + newBase64RemainderCount; // adjust to allow for the size of the new decoded data (+ remainder)

     delete[] decodedBytes;

      }

      fBase64RemainderCount = newBase64RemainderCount;

      if (fBase64RemainderCount > 0)break; // because we know that we have more input bytes still to receive

    }

   

    // Look for the end of the message: <CR><LF><CR><LF>

    unsigned char *tmpPtr = fLastCRLF + 2;

    if (tmpPtr <fRequestBuffer) tmpPtr =fRequestBuffer;

    while (tmpPtr < &ptr[newBytesRead-1]) {

      if (*tmpPtr =='\r' && *(tmpPtr+1) =='\n') {

     if (tmpPtr -fLastCRLF == 2) { // This is it:

       endOfMsg = True;

       break;

     }

     fLastCRLF = tmpPtr;

      }

      ++tmpPtr;

    }

   

    fRequestBufferBytesLeft -=newBytesRead;

    fRequestBytesAlreadySeen +=newBytesRead;

   

    if (!endOfMsg)break; // subsequent reads will be needed to complete the request

   

    // Parse the request string into command name and 'CSeq', then handle the command:

    fRequestBuffer[fRequestBytesAlreadySeen] ='\0';

    char cmdName[RTSP_PARAM_STRING_MAX];

    char urlPreSuffix[RTSP_PARAM_STRING_MAX];

    char urlSuffix[RTSP_PARAM_STRING_MAX];

    char cseq[RTSP_PARAM_STRING_MAX];

    char sessionIdStr[RTSP_PARAM_STRING_MAX];

    unsigned contentLength = 0;

fLastCRLF[2] = '\0'; // temporarily, for parsing

//解析客户端的Rtsp请求

    Boolean parseSucceeded = parseRTSPRequestString((char*)fRequestBuffer,fLastCRLF+2 - fRequestBuffer,

                                cmdName,sizeof cmdName,

                                urlPreSuffix,sizeof urlPreSuffix,

                                urlSuffix,sizeof urlSuffix,

                                cseq,sizeof cseq,

                                sessionIdStr,sizeof sessionIdStr,

                                contentLength);

    fLastCRLF[2] = '\r'; // restore its value

    if (parseSucceeded) {

#ifdef DEBUG

      fprintf(stderr, "parseRTSPRequestString() succeeded, returning cmdName \"%s\", urlPreSuffix \"%s\", urlSuffix \"%s\", CSeq \"%s\", Content-Length %u, with %d bytes following the message.\n", cmdName, urlPreSuffix, urlSuffix, cseq, contentLength, ptr + newBytesRead - (tmpPtr + 2));

#endif

      // If there was a "Content-Length:" header, then make sure we've received all of the data that it specified:

      if (ptr +newBytesRead < tmpPtr + 2 +contentLength) break;// we still need more data; subsequent reads will give it to us

     

      // We now have a complete RTSP request.

      // Handle the specified command (beginning by checking those that don't require session ids):

      fCurrentCSeq = cseq;

 

//请求是Opinion吗,处理Opinion消息

      if (strcmp(cmdName,"OPTIONS") == 0) {

     // If the request included a "Session:" id, and it refers to a client session that's current ongoing, then use this

     // command to indicate 'liveness' on that client session:

     if (sessionIdStr[0] !='\0') {

       clientSession = (RTSPServer::RTSPClientSession*)(fOurServer.fClientSessions->Lookup(sessionIdStr));

       if (clientSession !=NULL) clientSession->noteLiveness();

     }

     handleCmd_OPTIONS();

      } else if (urlPreSuffix[0] == '\0' && urlSuffix[0] == '*' && urlSuffix[1] == '\0') {

     // The special "*" URL means: an operation on the entire server. This works only for GET_PARAMETER and SET_PARAMETER:

     //获取参数和设置参数

     if (strcmp(cmdName,"GET_PARAMETER") == 0) {

       handleCmd_GET_PARAMETER((charconst*)fRequestBuffer);

     } else if (strcmp(cmdName,"SET_PARAMETER") == 0) {

       handleCmd_SET_PARAMETER((charconst*)fRequestBuffer);

     } else {

       handleCmd_notSupported();

     }

     //Describe 命令

      } else if (strcmp(cmdName,"DESCRIBE") == 0) {

     handleCmd_DESCRIBE(urlPreSuffix,urlSuffix, (charconst*)fRequestBuffer);

     //Setup请求

      } else if (strcmp(cmdName,"SETUP") == 0) {

     if (sessionIdStr[0] =='\0') {

       // No session id was present in the request. So create a new "RTSPClientSession" object for this request.

       // Choose a random (unused) 32-bit integer for the session id (it will be encoded as a 8-digit hex number).

       // (We avoid choosing session id 0, because that has a special use (by "OnDemandServerMediaSubsession").)

       u_int32_t sessionId;

       do {

         sessionId = (u_int32_t)our_random32();

         sprintf(sessionIdStr,"%08X", sessionId);

       } while (sessionId == 0 ||fOurServer.fClientSessions->Lookup(sessionIdStr) !=NULL);

       clientSession = fOurServer.createNewClientSession(sessionId);

       fOurServer.fClientSessions->Add(sessionIdStr,clientSession);

     } else {

       // The request included a session id. Make sure it's one that we have already set up:

       clientSession = (RTSPServer::RTSPClientSession*)(fOurServer.fClientSessions->Lookup(sessionIdStr));

 

       if (clientSession ==NULL) {

         handleCmd_sessionNotFound();

       }

     }

     if (clientSession !=NULL) clientSession->handleCmd_SETUP(this,urlPreSuffix, urlSuffix, (charconst*)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) {

     RTSPServer::RTSPClientSession*clientSession

       = sessionIdStr[0] == '\0' ? NULL : (RTSPServer::RTSPClientSession*)(fOurServer.fClientSessions->Lookup(sessionIdStr));

     if (clientSession ==NULL) {

       handleCmd_sessionNotFound();

     } else {

       clientSession->handleCmd_withinSession(this,cmdName, urlPreSuffix,urlSuffix, (charconst*)fRequestBuffer);

     }

      } else if (strcmp(cmdName,"REGISTER") == 0 || strcmp(cmdName, "REGISTER_REMOTE") == 0) {

     // Because - unlike other commands - an implementation of these commands needs the entire URL, we re-parse the

     // command to get it:

     char* url = strDupSize((char*)fRequestBuffer);

     if (sscanf((char*)fRequestBuffer,"%*s %s", url) == 1) {

       handleCmd_REGISTER(url,urlSuffix, strcmp(cmdName,"REGISTER_REMOTE") == 0);

     } else {

       handleCmd_bad();

     }

     delete[] url;

      } else {

     // The command is one that we don't handle:

     handleCmd_notSupported();

      }

    } else {

#ifdef DEBUG

      fprintf(stderr, "parseRTSPRequestString() failed; checking now for HTTP commands (for RTSP-over-HTTP tunneling)...\n");

#endif

      // The request was not (valid) RTSP, but check for a special case: HTTP commands (for setting up RTSP-over-HTTP tunneling):

      char sessionCookie[RTSP_PARAM_STRING_MAX];

      char acceptStr[RTSP_PARAM_STRING_MAX];

      *fLastCRLF = '\0'; // temporarily, for parsing

      parseSucceeded = parseHTTPRequestString(cmdName, sizeof cmdName,

                             urlSuffix,sizeof urlPreSuffix,

                             sessionCookie,sizeof sessionCookie,

                             acceptStr,sizeof acceptStr);

      *fLastCRLF = '\r';

      if (parseSucceeded) {

#ifdef DEBUG

     fprintf(stderr, "parseHTTPRequestString() succeeded, returning cmdName \"%s\", urlSuffix \"%s\", sessionCookie \"%s\", acceptStr \"%s\"\n", cmdName, urlSuffix, sessionCookie, acceptStr);

#endif

     // Check that the HTTP command is valid for RTSP-over-HTTP tunneling: There must be a 'session cookie'.

     Boolean isValidHTTPCmd = True;

     if (sessionCookie[0] =='\0') {

       // There was no "x-sessioncookie:" header. If there was an "Accept: application/x-rtsp-tunnelled" header,

       // then this is a bad tunneling request. Otherwise, assume that it's an attempt to access the stream via HTTP.

       if (strcmp(acceptStr,"application/x-rtsp-tunnelled") == 0) {

         isValidHTTPCmd = False;

       } else {

         handleHTTPCmd_StreamingGET(urlSuffix, (charconst*)fRequestBuffer);

       }

     } else if (strcmp(cmdName,"GET") == 0) {

       handleHTTPCmd_TunnelingGET(sessionCookie);

     } else if (strcmp(cmdName,"POST") == 0) {

       // We might have received additional data following the HTTP "POST" command - i.e., the first Base64-encoded RTSP command.

       // Check for this, and handle it if it exists:

       unsigned char const* extraData =fLastCRLF+4;

       unsigned extraDataSize = &fRequestBuffer[fRequestBytesAlreadySeen] -extraData;

       if (handleHTTPCmd_TunnelingPOST(sessionCookie,extraData, extraDataSize)) {

         // We don't respond to the "POST" command, and we go away:

         fIsActive = False;

         break;

       }

     } else {

       isValidHTTPCmd = False;

     }

     if (!isValidHTTPCmd) {

       handleHTTPCmd_notSupported();

     }

      } else {

#ifdef DEBUG

     fprintf(stderr, "parseHTTPRequestString() failed!\n");

#endif

     handleCmd_bad();

      }

    }

   

#ifdef DEBUG

    fprintf(stderr, "sending response: %s", fResponseBuffer);

#endif

    send(fClientOutputSocket, (charconst*)fResponseBuffer,strlen((char*)fResponseBuffer), 0);

   

    if (clientSession !=NULL && clientSession->fStreamAfterSETUP &&strcmp(cmdName,"SETUP") == 0) {

      // The client has asked for streaming to commence now, rather than after a

      // subsequent "PLAY" command.  So, simulate the effect of a "PLAY" command:

     //PLAY命令,收到setup后Play

      clientSession->handleCmd_withinSession(this,"PLAY", urlPreSuffix,urlSuffix, (charconst*)fRequestBuffer);

    }

   

    // Check whether there are extra bytes remaining in the buffer, after the end of the request (a rare case).

    // If so, move them to the front of our buffer, and keep processing it, because it might be a following, pipelined request.

    unsigned requestSize = (fLastCRLF+4-fRequestBuffer) +contentLength;

    numBytesRemaining = fRequestBytesAlreadySeen - requestSize;

    resetRequestBuffer(); // to prepare for any subsequent request

 

    if (numBytesRemaining > 0) {

      memmove(fRequestBuffer, &fRequestBuffer[requestSize],numBytesRemaining);

      newBytesRead = numBytesRemaining;

    }

  } while (numBytesRemaining > 0);

 

  --fRecursionCount;

  if (!fIsActive) {

    if (fRecursionCount > 0)closeSockets(); elsedelete this;

    // Note: The "fRecursionCount" test is for a pathological situation where we reenter the event loop and get called recursively

    // while handling a command (e.g., while handling a "DESCRIBE", to get a SDP description).

    // In such a case we don't want to actually delete ourself until we leave the outermost call.

  }

}

 

创建完socket后;建立Rtsp服务器(ServerMediaSession):

 

{

         char const* streamName = "h264ESVideoTest";

         char const* inputFileName = "test.264";

         ServerMediaSession* sms

              = ServerMediaSession::createNew(*env,streamName, streamName,

              descriptionString);

         sms->addSubsession(H264VideoFileServerMediaSubsession

              ::createNew(*env,inputFileName, reuseFirstSource));

         rtspServer->addServerMediaSession(sms);

 

         announceStream(rtspServer,sms, streamName,inputFileName);

     }

 

首先建立sms会话,然后增加子会话:

 

建立视频文件子会话:

H264VideoFileServerMediaSubsession*

H264VideoFileServerMediaSubsession::createNew(UsageEnvironment&env,

                             charconst* fileName,

                             BooleanreuseFirstSource) {

  return new H264VideoFileServerMediaSubsession(env,fileName, reuseFirstSource);

}

 

//

H264VideoFileServerMediaSubsession::H264VideoFileServerMediaSubsession(UsageEnvironment&env,

                                            char const* fileName,Boolean reuseFirstSource)

  : FileServerMediaSubsession(env,fileName, reuseFirstSource),

    fAuxSDPLine(NULL),fDoneFlag(0), fDummyRTPSink(NULL) {

}

 

将子会话插入到ServerMediaSession的链表中;

Boolean

ServerMediaSession::addSubsession(ServerMediaSubsession*subsession) {

  if (subsession->fParentSession !=NULL) return False; // it's already used

 

  if (fSubsessionsTail ==NULL) {

    fSubsessionsHead = subsession;

  } else {

    fSubsessionsTail->fNext =subsession;

  }

  fSubsessionsTail = subsession;

 

  subsession->fParentSession =this;

  subsession->fTrackNumber = ++fSubsessionCounter;

  return True;

}

 

    

rtspServer->addServerMediaSession(sms);将sms保存Hash表中;

 

 

void RTSPServer::addServerMediaSession(ServerMediaSession*serverMediaSession) {

  if (serverMediaSession ==NULL) return;

 

  char const*sessionName = serverMediaSession->streamName();

  if (sessionName ==NULL) sessionName ="";

  removeServerMediaSession(sessionName);// in case an existing "ServerMediaSession" with this name already exists

 

  fServerMediaSessions->Add(sessionName, (void*)serverMediaSession);

}

 

 

最后调用announceStream函数

static voidannounceStream(RTSPServer*rtspServer, ServerMediaSession*sms,

                               charconst* streamName,char const* inputFileName) {

                                    char*url = rtspServer->rtspURL(sms);

                                    UsageEnvironment&env = rtspServer->envir();

                                    env <<"\n\"" << streamName <<"\" stream, from the file \""

                                        << inputFileName << "\"\n";

                                    env <<"Play this stream using the URL \"" << url << "\"\n";

                                    delete[]url;

}

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值