1. 程式人生 > >RTSP伺服器運作

RTSP伺服器運作

RTSP服務運作


基礎基本搞明白了,那麼RTSP,RTP等這些協議又是如何利用這些基礎機制運作的呢?
首先來看RTSP.


RTSP首先需建立TCP偵聽socket。可見於此函式:
  1. DynamicRTSPServer* DynamicRTSPServer::createNew(UsageEnvironment& env, Port ourPort,  
  2. UserAuthenticationDatabase* authDatabase,  
  3. unsigned reclamationTestSeconds) {  
  4. int ourSocket = setUpOurSocket(env, ourPort); //建立TCP socket
  5. if (ourSocket == -1)  
  6. return NULL;  
  7. returnnew DynamicRTSPServer(env, ourSocket, ourPort, authDatabase,  
  8. reclamationTestSeconds);  
  9. }  
  1. DynamicRTSPServer* DynamicRTSPServer::createNew(UsageEnvironment& env, Port ourPort,  
  2. UserAuthenticationDatabase* authDatabase,  
  3. unsigned reclamationTestSeconds) {  
  4. int ourSocket = setUpOurSocket(env, ourPort); //建立TCP socket
  5. if (ourSocket == -1)  
  6. return NULL;  
  7. returnnew DynamicRTSPServer(env, ourSocket, ourPort, authDatabase,  
  8. reclamationTestSeconds);  
  9. }  

要幀聽客戶端的連線,就需要利用任務排程機制了,所以需新增一個socket handler。可見於此函式:
  1. RTSPServer::RTSPServer(UsageEnvironment& env,   
  2. int
     ourSocket,   
  3.         Port ourPort,  
  4.         UserAuthenticationDatabase* authDatabase,  
  5.         unsigned reclamationTestSeconds) :  
  6.         Medium(env),   
  7.         fRTSPServerSocket(ourSocket),  
  8.         fRTSPServerPort(ourPort),  
  9.         fHTTPServerSocket(-1),  
  10.         fHTTPServerPort(0),  
  11.         fClientSessionsForHTTPTunneling(NULL),   
  12.         fAuthDB(authDatabase),  
  13.         fReclamationTestSeconds(reclamationTestSeconds),  
  14.         fServerMediaSessions(HashTable::create(STRING_HASH_KEYS))   
  15. {  
  16. #ifdef USE_SIGNALS
  17. // Ignore the SIGPIPE signal, so that clients on the same host that are killed
  18. // don't also kill us:
  19.     signal(SIGPIPE, SIG_IGN);  
  20. #endif
  21. // Arrange to handle connections from others:
  22.     env.taskScheduler().turnOnBackgroundReadHandling(  
  23.             fRTSPServerSocket,  
  24.             (TaskScheduler::BackgroundHandlerProc*) &incomingConnectionHandlerRTSP,  
  25. this);  
  26. }  
  1. RTSPServer::RTSPServer(UsageEnvironment& env,   
  2.         int ourSocket,   
  3.         Port ourPort,  
  4.         UserAuthenticationDatabase* authDatabase,  
  5.         unsigned reclamationTestSeconds) :  
  6.         Medium(env),   
  7.         fRTSPServerSocket(ourSocket),  
  8.         fRTSPServerPort(ourPort),  
  9.         fHTTPServerSocket(-1),  
  10.         fHTTPServerPort(0),  
  11.         fClientSessionsForHTTPTunneling(NULL),   
  12.         fAuthDB(authDatabase),  
  13.         fReclamationTestSeconds(reclamationTestSeconds),  
  14.         fServerMediaSessions(HashTable::create(STRING_HASH_KEYS))   
  15. {  
  16. #ifdef USE_SIGNALS
  17.     // Ignore the SIGPIPE signal, so that clients on the same host that are killed
  18.     // don't also kill us:
  19.     signal(SIGPIPE, SIG_IGN);  
  20. #endif
  21.     // Arrange to handle connections from others:
  22.     env.taskScheduler().turnOnBackgroundReadHandling(  
  23.             fRTSPServerSocket,  
  24.             (TaskScheduler::BackgroundHandlerProc*) &incomingConnectionHandlerRTSP,  
  25.             this);  
  26. }  


當收到客戶的連線時需儲存下代表客戶端的新socket,以後用這個socket與這個客戶通訊。每個客戶將來會對應一個rtp會話,而且各客戶的RTSP請求只控制自己的rtp會話,那麼最好建立一個會話類,代表各客戶的rtsp會話。於是類RTSPServer::RTSPClientSession產生,它儲存的代表客戶的socket。下為RTSPClientSession的建立過程

  1. void RTSPServer::incomingConnectionHandler(int serverSocket)   
  2. {  
  3. struct sockaddr_in clientAddr;  
  4.     SOCKLEN_T clientAddrLen = sizeof clientAddr;  
  5. //接受連線
  6. int clientSocket = accept(serverSocket,  
  7.             (struct sockaddr*) &clientAddr,  
  8.             &clientAddrLen);  
  9. if (clientSocket < 0) {  
  10. int err = envir().getErrno();  
  11. if (err != EWOULDBLOCK) {  
  12.             envir().setResultErrMsg("accept() failed: ");  
  13.         }  
  14. return;  
  15.     }  
  16. //設定socket的引數
  17.     makeSocketNonBlocking(clientSocket);  
  18.     increaseSendBufferTo(envir(), clientSocket, 50 * 1024);  
  19. #ifdef DEBUG
  20.     envir() << "accept()ed connection from " << our_inet_ntoa(clientAddr.sin_addr) << "\n";  
  21. #endif
  22. //產生一個sesson id
  23. // Create a new object for this RTSP session.
  24. // (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
  25. //  a collision; the probability of two concurrent sessions getting the same session id is very low.)
  26. // (We do, however, avoid choosing session id 0, because that has a special use (by "OnDemandServerMediaSubsession").)
  27.     unsigned sessionId;  
  28. do {  
  29.         sessionId = (unsigned) our_random();  
  30.     } while (sessionId == 0);  
  31. //建立RTSPClientSession,注意傳入的引數
  32.     (void) createNewClientSession(sessionId, clientSocket, clientAddr);  
  33. }  
  1. void RTSPServer::incomingConnectionHandler(int serverSocket)   
  2. {  
  3.     struct sockaddr_in clientAddr;  
  4.     SOCKLEN_T clientAddrLen = sizeof clientAddr;  
  5.     //接受連線
  6.     int clientSocket = accept(serverSocket,  
  7.             (struct sockaddr*) &clientAddr,  
  8.             &clientAddrLen);  
  9.     if (clientSocket < 0) {  
  10.         int err = envir().getErrno();  
  11.         if (err != EWOULDBLOCK) {  
  12.             envir().setResultErrMsg("accept() failed: ");  
  13.         }  
  14.         return;  
  15.     }  
  16.     //設定socket的引數
  17.     makeSocketNonBlocking(clientSocket);  
  18.     increaseSendBufferTo(envir(), clientSocket, 50 * 1024);  
  19. #ifdef DEBUG
  20.     envir() << "accept()ed connection from " << our_inet_ntoa(clientAddr.sin_addr) << "\n";  
  21. #endif
  22.     //產生一個sesson id
  23.     // Create a new object for this RTSP session.
  24.     // (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
  25.     //  a collision; the probability of two concurrent sessions getting the same session id is very low.)
  26.     // (We do, however, avoid choosing session id 0, because that has a special use (by "OnDemandServerMediaSubsession").)
  27.     unsigned sessionId;  
  28.     do {  
  29.         sessionId = (unsigned) our_random();  
  30.     } while (sessionId == 0);  
  31.     //建立RTSPClientSession,注意傳入的引數
  32.     (void) createNewClientSession(sessionId, clientSocket, clientAddr);  
  33. }  

 RTSPClientSession要提供什麼功能呢?可以想象:需要監聽客戶端的rtsp請求並回應它,需要在DESCRIBE請求中返回所請求的流的資訊,需要在SETUP請求中建立起RTP會話,需要在TEARDOWN請求中關閉RTP會話,等等...

RTSPClientSession要偵聽客戶端的請求,就需把自己的socket handler加入計劃任務。證據如下:
  1. RTSPServer::RTSPClientSession::RTSPClientSession(  
  2.             RTSPServer& ourServer,  
  3.             unsigned sessionId,  
  4. int clientSocket,  
  5. struct sockaddr_in clientAddr) :  
  6.         fOurServer(ourServer),  
  7.         fOurSessionId(sessionId),  
  8.         fOurServerMediaSession(NULL),  
  9.         fClientInputSocket(clientSocket),  
  10.         fClientOutputSocket(clientSocket),  
  11.         fClientAddr(clientAddr),  
  12.         fSessionCookie(NULL),  
  13.         fLivenessCheckTask(NULL),  
  14.         fIsMulticast(False),  
  15.         fSessionIsActive(True),  
  16.         fStreamAfterSETUP(False),  
  17.         fTCPStreamIdCount(0),  
  18.         fNumStreamStates(0),  
  19.         fStreamStates(NULL),  
  20.         fRecursionCount(0)  
  21. {  
  22. // Arrange to handle incoming requests:
  23.     resetRequestBuffer();  
  24.     envir().taskScheduler().turnOnBackgroundReadHandling(fClientInputSocket,  
  25.             (TaskScheduler::BackgroundHandlerProc*) &incomingRequestHandler,  
  26. this);  
  27.     noteLiveness();  
  28. }  
  1. RTSPServer::RTSPClientSession::RTSPClientSession(  
  2.             RTSPServer& ourServer,  
  3.             unsigned sessionId,  
  4.             int clientSocket,  
  5.             struct sockaddr_in clientAddr) :  
  6.         fOurServer(ourServer),  
  7.         fOurSessionId(sessionId),  
  8.         fOurServerMediaSession(NULL),  
  9.         fClientInputSocket(clientSocket),  
  10.         fClientOutputSocket(clientSocket),  
  11.         fClientAddr(clientAddr),  
  12.         fSessionCookie(NULL),  
  13.         fLivenessCheckTask(NULL),  
  14.         fIsMulticast(False),  
  15.         fSessionIsActive(True),  
  16.         fStreamAfterSETUP(False),  
  17.         fTCPStreamIdCount(0),  
  18.         fNumStreamStates(0),  
  19.         fStreamStates(NULL),  
  20.         fRecursionCount(0)  
  21. {  
  22.     // Arrange to handle incoming requests:
  23.     resetRequestBuffer();  
  24.     envir().taskScheduler().turnOnBackgroundReadHandling(fClientInputSocket,  
  25.             (TaskScheduler::BackgroundHandlerProc*) &incomingRequestHandler,  
  26.             this);  
  27.     noteLiveness();  
  28. }  


下面重點講一下下RTSPClientSession響應DESCRIBE請求的過程:
  1. void RTSPServer::RTSPClientSession::handleCmd_DESCRIBE(  
  2. charconst* cseq,  
  3. charconst* urlPreSuffix,  
  4. charconst* urlSuffix,  
  5. charconst* fullRequestStr)  
  6. {  
  7. char* sdpDescription = NULL;  
  8. char* rtspURL = NULL;  
  9. do {  
  10. //整理一下下RTSP地址
  11. char urlTotalSuffix[RTSP_PARAM_STRING_MAX];  
  12. if (strlen(urlPreSuffix) + strlen(urlSuffix) + 2  
  13.                 > sizeof urlTotalSuffix) {  
  14.             handleCmd_bad(cseq);  
  15. break;  
  16.         }  
  17.         urlTotalSuffix[0] = '\0';  
  18. if (urlPreSuffix[0] != '\0') {  
  19.             strcat(urlTotalSuffix, urlPreSuffix);  
  20.             strcat(urlTotalSuffix, "/");  
  21.         }  
  22.         strcat(urlTotalSuffix, urlSuffix);  
  23. //驗證帳戶和密碼
  24. if (!authenticationOK("DESCRIBE", cseq, urlTotalSuffix, fullRequestStr))  
  25. break;  
  26. // We should really check that the request contains an "Accept:" #####
  27. // for "application/sdp", because that's what we're sending back #####
  28. // Begin by looking up the "ServerMediaSession" object for the specified "urlTotalSuffix":
  29. //跟據流的名字查詢ServerMediaSession,如果找不到,會建立一個。每個ServerMediaSession中至少要包含一個
  30. //ServerMediaSubsession。一個ServerMediaSession對應一個媒體,可以認為是Server上的一個檔案,或一個實時獲取裝置。其包含的每個ServerMediaSubSession代表媒體中的一個Track。所以一個ServerMediaSession對應一個媒體,如果客戶請求的媒體名相同,就使用已存在的ServerMediaSession,如果不同,就建立一個新的。一個流對應一個StreamState,StreamState與ServerMediaSubsession相關,但代表的是動態的,而ServerMediaSubsession代表靜態的。
  31.         ServerMediaSession* session = fOurServer.lookupServerMediaSession(urlTotalSuffix);  
  32. if (session == NULL) {  
  33.             handleCmd_notFound(cseq);  
  34. break;  
  35.         }  
  36. // Then, assemble a SDP description for this session:
  37. //獲取SDP字串,在函式內會依次獲取每個ServerMediaSubSession的字串然連線起來。
  38.         sdpDescription = session->generateSDPDescription();  
  39. if (sdpDescription == NULL) {  
  40. // This usually means that a file name that was specified for a
  41. // "ServerMediaSubsession" does not exist.
  42.             snprintf((char*) fResponseBuffer, sizeof fResponseBuffer,  
  43. "RTSP/1.0 404 File Not Found, Or In Incorrect Format\r\n"
  44. "CSeq: %s\r\n"
  45. "%s\r\n", cseq, dateHeader());  
  46. break;  
  47.         }  
  48.         unsigned sdpDescriptionSize = strlen(sdpDescription);  
  49. // Also, generate our RTSP URL, for the "Content-Base:" header
  50. // (which is necessary to ensure that the correct URL gets used in
  51. // subsequent "SETUP" requests).
  52.         rtspURL = fOurServer.rtspURL(session, fClientInputSocket);  
  53. //形成響應DESCRIBE請求的RTSP字串。
  54.         snprintf((char*) fResponseBuffer, sizeof fResponseBuffer,  
  55. "RTSP/1.0 200 OK\r\nCSeq: %s\r\n"
  56. "%s"
  57. "Content-Base: %s/\r\n"
  58. "Content-Type: application/sdp\r\n"
  59. "Content-Length: %d\r\n\r\n"
  60. "%s", cseq, dateHeader(), rtspURL, sdpDescriptionSize,  
  61.                 sdpDescription);  
  62.     } while (0);  
  63. delete[] sdpDescription;  
  64. delete[] rtspURL;  
  65. //返回後會被立即傳送(沒有把socket write操作放入計劃任務中)。
  66. }  
  1. void RTSPServer::RTSPClientSession::handleCmd_DESCRIBE(  
  2.         charconst* cseq,  
  3.         charconst* urlPreSuffix,  
  4.         charconst* urlSuffix,  
  5.         charconst* fullRequestStr)  
  6. {  
  7.     char* sdpDescription = NULL;  
  8.     char* rtspURL = NULL;  
  9.     do {  
  10.         //整理一下下RTSP地址
  11.         char urlTotalSuffix[RTSP_PARAM_STRING_MAX];  
  12.         if (strlen(urlPreSuffix) + strlen(urlSuffix) + 2  
  13.                 > sizeof urlTotalSuffix) {  
  14.             handleCmd_bad(cseq);  
  15.             break;  
  16.         }  
  17.         urlTotalSuffix[0] = '\0';  
  18.         if (urlPreSuffix[0] != '\0') {  
  19.             strcat(urlTotalSuffix, urlPreSuffix);  
  20.             strcat(urlTotalSuffix, "/");  
  21.         }  
  22.         strcat(urlTotalSuffix, urlSuffix);  
  23.         //驗證帳戶和密碼
  24.         if (!authenticationOK("DESCRIBE", cseq, urlTotalSuffix, fullRequestStr))  
  25.             break;  
  26.         // We should really check that the request contains an "Accept:" #####
  27.         // for "application/sdp", because that's what we're sending back #####
  28.         // Begin by looking up the "ServerMediaSession" object for the specified "urlTotalSuffix":
  29.         //跟據流的名字查詢ServerMediaSession,如果找不到,會建立一個。每個ServerMediaSession中至少要包含一個
  30.         //ServerMediaSubsession。一個ServerMediaSession對應一個媒體,可以認為是Server上的一個檔案,或一個實時獲取裝置。其包含的每個ServerMediaSubSession代表媒體中的一個Track。所以一個ServerMediaSession對應一個媒體,如果客戶請求的媒體名相同,就使用已存在的ServerMediaSession,如果不同,就建立一個新的。一個流對應一個StreamState,StreamState與ServerMediaSubsession相關,但代表的是動態的,而ServerMediaSubsession代表靜態的。
  31.         ServerMediaSession* session = fOurServer.lookupServerMediaSession(urlTotalSuffix);  
  32.         if (session == NULL) {  
  33.             handleCmd_notFound(cseq);  
  34.             break;  
  35.         }  
  36.         // Then, assemble a SDP description for this session:
  37.         //獲取SDP字串,在函式內會依次獲取每個ServerMediaSubSession的字串然連線起來。
  38.         sdpDescription = session->generateSDPDescription();  
  39.         if (sdpDescription == NULL) {  
  40.             // This usually means that a file name that was specified for a
  41.             // "ServerMediaSubsession" does not exist.
  42.             snprintf((char*) fResponseBuffer, sizeof fResponseBuffer,  
  43.                     "RTSP/1.0 404 File Not Found, Or In Incorrect Format\r\n"
  44.                     "CSeq: %s\r\n"
  45.                     "%s\r\n", cseq, dateHeader());  
  46.             break;  
  47.         }  
  48.         unsigned sdpDescriptionSize = strlen(sdpDescription);  
  49.         // Also, generate our RTSP URL, for the "Content-Base:" header
  50.         // (which is necessary to ensure that the correct URL gets used in
  51.         // subsequent "SETUP" requests).
  52.         rtspURL = fOurServer.rtspURL(session, fClientInputSocket);  
  53.         //形成響應DESCRIBE請求的RTSP字串。
  54.         snprintf((char*) fResponseBuffer, sizeof fResponseBuffer,  
  55.                 "RTSP/1.0 200 OK\r\nCSeq: %s\r\n"
  56.                 "%s"
  57.                 "Content-Base: %s/\r\n"
  58.                 "Content-Type: application/sdp\r\n"
  59.                 "Content-Length: %d\r\n\r\n"
  60.                 "%s", cseq, dateHeader(), rtspURL, sdpDescriptionSize,  
  61.                 sdpDescription);  
  62.     } while (0);  
  63.     delete[] sdpDescription;  
  64.     delete[] rtspURL;  
  65.     //返回後會被立即傳送(沒有把socket write操作放入計劃任務中)。
  66. }  



fOurServer.lookupServerMediaSession(urlTotalSuffix)中會在找不到同名ServerMediaSession時新建一個,代表一個RTP流的ServerMediaSession們是被RTSPServer管理的,而不是被RTSPClientSession擁有。為什麼呢?因為ServerMediaSession代表的是一個靜態的流,也就是可以從它裡面獲取一個流的各種資訊,但不能獲取傳輸狀態。不同客戶可能連線到同一個流,所以ServerMediaSession應被RTSPServer所擁有。建立一個ServerMediaSession過程值得一觀:
  1. static ServerMediaSession* createNewSMS(UsageEnvironment& env,charconst* fileName, FILE/*fid*/)   
  2. {  
  3. // Use the file name extension to determine the type of "ServerMediaSession":
  4. charconst* extension = strrchr(fileName, '.');  
  5. if (extension == NULL)  
  6. return NULL;  
  7.     ServerMediaSession* sms = NULL;  
  8.     Boolean const reuseSource = False;  
  9. if (strcmp(extension, ".aac") == 0) {  
  10. // Assumed to be an AAC Audio (ADTS format) file:
  11.         NEW_SMS("AAC Audio");  
  12.         sms->addSubsession(  
  13.                 ADTSAudioFileServerMediaSubsession::createNew(env, fileName,  
  14.                         reuseSource));  
  15.     } elseif (strcmp(extension, ".amr") == 0) {  
  16. // Assumed to be an AMR Audio file:
  17.         NEW_SMS("AMR Audio");  
  18.         sms->addSubsession(  
  19.                 AMRAudioFileServerMediaSubsession::createNew(env, fileName,  
  20.                         reuseSource));  
  21.     } elseif (strcmp(extension, ".ac3") == 0) {  
  22. // Assumed to be an AC-3 Audio file:
  23.         NEW_SMS("AC-3 Audio");  
  24.         sms->addSubsession(  
  25.                 AC3AudioFileServerMediaSubsession::createNew(env, fileName,  
  26.                         reuseSource));  
  27.     } elseif (strcmp(extension, ".m4e") == 0) {  
  28. // Assumed to be a MPEG-4 Video Elementary Stream file:
  29.         NEW_SMS("MPEG-4 Video");  
  30.         sms->addSubsession(  
  31.                 MPEG4VideoFileServerMediaSubsession::createNew(env, fileName,  
  32.                         reuseSource));  
  33.     } elseif (strcmp(extension, ".264") == 0) {  
  34. // Assumed to be a H.264 Video Elementary Stream file:
  35.         NEW_SMS("H.264 Video");  
  36.         OutPacketBuffer::maxSize = 100000; // allow for some possibly large H.264 frames
  37.         sms->addSubsession(  
  38.                 H264VideoFileServerMediaSubsession::createNew(env, fileName,  
  39.                         reuseSource));  
  40.     } elseif (strcmp(extension, ".mp3") == 0) {  
  41. // Assumed to be a MPEG-1 or 2 Audio file:
  42.         NEW_SMS("MPEG-1 or 2 Audio");  
  43. // To stream using 'ADUs' rather than raw MP3 frames, uncomment the following:
  44. //#define STREAM_USING_ADUS 1
  45. // To also reorder ADUs before streaming, uncomment the following:
  46. //#define INTERLEAVE_ADUS 1
  47. // (For more information about ADUs and interleaving,
  48. //  see <http://www.live555.com/rtp-mp3/>)
  49.         Boolean useADUs = False;  
  50.         Interleaving* interleaving = NULL;  
  51. #ifdef STREAM_USING_ADUS
  52.         useADUs = True;  
  53. #ifdef INTERLEAVE_ADUS
  54.         unsigned char interleaveCycle[] = {0,2,1,3}; // or choose your own...
  55.         unsigned const interleaveCycleSize  
  56.         = (sizeof interleaveCycle)/(sizeof (unsigned char));  
  57.         interleaving = new Interleaving(interleaveCycleSize, interleaveCycle);  
  58. #endif
  59. #endif
  60.         sms->addSubsession(  
  61.                 MP3AudioFileServerMediaSubsession::createNew(env, fileName,  
  62.                         reuseSource, useADUs, interleaving));  
  63.     } elseif (strcmp(extension, ".mpg") == 0) {  
  64. // Assumed to be a MPEG-1 or 2 Program Stream (audio+video) file:
  65.         NEW_SMS("MPEG-1 or 2 Program Stream");  
  66.         MPEG1or2FileServerDemux* demux = MPEG1or2FileServerDemux::createNew(env,  
  67.                 fileName, reuseSource);  
  68.         sms->addSubsession(demux->newVideoServerMediaSubsession());  
  69.         sms->addSubsession(demux->newAudioServerMediaSubsession());  
  70.     } elseif (strcmp(extension, ".ts") == 0) {  
  71. // Assumed to be a MPEG Transport Stream file:
  72. // Use an index file name that's the same as the TS file name, except with ".tsx":
  73.         unsigned indexFileNameLen = strlen(fileName) + 2; // allow for trailing "x\0"
  74. char* indexFileName = newchar[indexFileNameLen];  
  75.         sprintf(indexFileName, "%sx", fileName);  
  76.         NEW_SMS("MPEG Transport Stream");  
  77.         sms->addSubsession(  
  78.                 MPEG2TransportFileServerMediaSubsession::createNew(env,  
  79.                         fileName, indexFileName, reuseSource));  
  80. delete[] indexFileName;  
  81.     } elseif (strcmp(extension, ".wav") == 0) {  
  82. // Assumed to be a WAV Audio file:
  83.         NEW_SMS("WAV Audio Stream");  
  84. // To convert 16-bit PCM data to 8-bit u-law, prior to streaming,
  85. // change the following to True:
  86.         Boolean convertToULaw = False;  
  87.         sms->addSubsession(  
  88.                 WAVAudioFileServerMediaSubsession::createNew(env, fileName,  
  89.                         reuseSource, convertToULaw));  
  90.     } elseif (strcmp(extension, ".dv") == 0) {  
  91. // Assumed to be a DV Video file
  92. // First, make sure that the RTPSinks' buffers will be large enough to handle the huge size of DV frames (as big as 288000).
  93.         OutPacketBuffer::maxSize = 300000;  
  94.         NEW_SMS("DV Video");  
  95.         sms->addSubsession(  
  96.                 DVVideoFileServerMediaSubsession::createNew(env, fileName,  
  97.                         reuseSource));  
  98.     } elseif (strcmp(extension, ".mkv") == 0) {  
  99. // Assumed to be a Matroska file
  100.         NEW_SMS("Matroska video+audio+(optional)subtitles");  
  101. // Create a Matroska file server demultiplexor for the specified file.  (We enter the event loop to wait for this to complete.)
  102.         newMatroskaDemuxWatchVariable = 0;  
  103.         MatroskaFileServerDemux::createNew(env, fileName,  
  104.                 onMatroskaDemuxCreation, NULL);  
  105.         env.taskScheduler().doEventLoop(&newMatroskaDemuxWatchVariable);  
  106.         ServerMediaSubsession* smss;  
  107. while ((smss = demux->newServerMediaSubsession()) != NULL) {  
  108.             sms->addSubsession(smss);  
  109.         }  
  110.     }  
  111. return sms;  
  112. }  
  1. static ServerMediaSession* createNewSMS(UsageEnvironment& env,charconst* fileName, FILE/*fid*/)   
  2. {  
  3.     // Use the file name extension to determine the type of "ServerMediaSession":
  4.     charconst* extension = strrchr(fileName, '.');  
  5.     if (extension == NULL)  
  6.         return NULL;  
  7.     ServerMediaSession* sms = NULL;  
  8.     Boolean const reuseSource = False;  
  9.     if (strcmp(extension, ".aac") == 0) {  
  10.         // Assumed to be an AAC Audio (ADTS format) file:
  11.         NEW_SMS("AAC Audio");  
  12.         sms->addSubsession(  
  13.                 ADTSAudioFileServerMediaSubsession::createNew(env, fileName,  
  14.                         reuseSource));  
  15.     } elseif (strcmp(extension, ".amr") == 0) {  
  16.         // Assumed to be an AMR Audio file:
  17.         NEW_SMS("AMR Audio");  
  18.         sms->addSubsession(  
  19.                 AMRAudioFileServerMediaSubsession::createNew(env, fileName,  
  20.                         reuseSource));  
  21.     } elseif (strcmp(extension, ".ac3") == 0) {  
  22.         // Assumed to be an AC-3 Audio file:
  23.         NEW_SMS("AC-3 Audio");  
  24.         sms->addSubsession(  
  25.                 AC3AudioFileServerMediaSubsession::createNew(env, fileName,  
  26.                         reuseSource));  
  27.     } elseif (strcmp(extension, ".m4e") == 0) {  
  28.         // Assumed to be a MPEG-4 Video Elementary Stream file:
  29.         NEW_SMS("MPEG-4 Video");  
  30.         sms->addSubsession(  
  31.                 MPEG4VideoFileServerMediaSubsession::createNew(env, fileName,  
  32.                         reuseSource));  
  33.     } elseif (strcmp(extension, ".264") == 0) {  
  34.         // Assumed to be a H.264 Video Elementary Stream file:
  35.         NEW_SMS("H.264 Video");  
  36.         OutPacketBuffer::maxSize = 100000; // allow for some possibly large H.264 frames
  37.         sms->addSubsession(  
  38.                 H264VideoFileServerMediaSubsession::createNew(env, fileName,  
  39.                         reuseSource));  
  40.     } elseif (strcmp(extension, ".mp3") == 0) {  
  41.         // Assumed to be a MPEG-1 or 2 Audio file:
  42.         NEW_SMS("MPEG-1 or 2 Audio");  
  43.         // To stream using 'ADUs' rather than raw MP3 frames, uncomment the following:
  44. //#define STREAM_USING_ADUS 1
  45.         // To also reorder ADUs before streaming, uncomment the following:
  46. //#define INTERLEAVE_ADUS 1
  47.         // (For more information about ADUs and interleaving,
  48.         //  see <http://www.live555.com/rtp-mp3/>)
  49.         Boolean useADUs = False;  
  50.         Interleaving* interleaving = NULL;  
  51. #ifdef STREAM_USING_ADUS
  52.         useADUs = True;  
  53. #ifdef INTERLEAVE_ADUS
  54.         unsigned char interleaveCycle[] = {0,2,1,3}; // or choose your own...
  55.         unsigned const interleaveCycleSize  
  56.         = (sizeof interleaveCycle)/(sizeof (unsigned char));  
  57.         interleaving = new Interleaving(interleaveCycleSize, interleaveCycle);  
  58. #endif
  59. #endif
  60.         sms->addSubsession(  
  61.                 MP3AudioFileServerMediaSubsession::createNew(env, fileName,  
  62.                         reuseSource, useADUs, interleaving));  
  63.     } elseif (strcmp(extension, ".mpg") == 0) {  
  64.         // Assumed to be a MPEG-1 or 2 Program Stream (audio+video) file:
  65.         NEW_SMS("MPEG-1 or 2 Program Stream");  
  66.         MPEG1or2FileServerDemux* demux = MPEG1or2FileServerDemux::createNew(env,  
  67.                 fileName, reuseSource);  
  68.         sms-