基础基本搞明白了,那么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
8
9 return new DynamicRTSPServer(env, ourSocket, ourPort, authDatabase,
10 reclamationTestSeconds);
11 }
要帧听客户端的连接,就需要利用任务调度机制了,所以需添加一个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
22
23 // Arrange to handle connections from others:
24 env.taskScheduler().turnOnBackgroundReadHandling(
25 fRTSPServerSocket,
26 (TaskScheduler::BackgroundHandlerProc*) &incomingConnectionHandlerRTSP,
27 this);
28 }
当收到客户的连接时需保存下代表客户端的新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 //接受连接
7 int clientSocket = accept(serverSocket,
8 (struct sockaddr*) &clientAddr,
9 &clientAddrLen);
10
11 if (clientSocket < 0) {
12 int err = envir().getErrno();
13 if (err != EWOULDBLOCK) {
14 envir().setResultErrMsg("accept() failed: ");
15 }
16 return;
17 }
18
19 //设置socket的参数
20 makeSocketNonBlocking(clientSocket);
21 increaseSendBufferTo(envir(), clientSocket, 50 * 1024);
22
23 #ifdef DEBUG
24 envir() << "accept()ed connection from " << our_inet_ntoa(clientAddr.sin_addr) << "\n";
25 #endif
26
27 //产生一个sesson id
28
29 // Create a new object for this RTSP session.
30 // (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
31 // a collision; the probability of two concurrent sessions getting the same session id is very low.)
32 // (We do, however, avoid choosing session id 0, because that has a special use (by "OnDemandServerMediaSubsession").)
33 unsigned sessionId;
34 do {
35 sessionId = (unsigned) our_random();
36 } while (sessionId == 0);
37
38 //创建RTSPClientSession,注意传入的参数
39 (void) createNewClientSession(sessionId, clientSocket, clientAddr);
40 }
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 }
下面重点讲一下下RTSPClientSession响应DESCRIBE请求的过程:
1 void RTSPServer::RTSPClientSession::handleCmd_DESCRIBE(
2 char const* cseq,
3 char const* urlPreSuffix,
4 char const* urlSuffix,
5 char const* 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
25 //验证帐户和密码
26 if (!authenticationOK("DESCRIBE", cseq, urlTotalSuffix, fullRequestStr))
27 break;
28
29
30 // We should really check that the request contains an "Accept:" #####
31 // for "application/sdp", because that's what we're sending back #####
32
33
34 // Begin by looking up the "ServerMediaSession" object for the specified "urlTotalSuffix":
35 //跟据流的名字查找ServerMediaSession,如果找不到,会创建一个。每个ServerMediaSession中至少要包含一个
36 //ServerMediaSubSession。一个ServerMediaSession代表一个rtp流,其包含的每个ServerMediaSubSession代表
37 //流中的一个Track。
38 ServerMediaSession* session = fOurServer.lookupServerMediaSession(urlTotalSuffix);
39 if (session == NULL) {
40 handleCmd_notFound(cseq);
41 break;
42 }
43
44
45 // Then, assemble a SDP description for this session:
46 //获取SDP字符串,在函数内会依次获取每个ServerMediaSubSession的字符串然连接起来。
47 sdpDescription = session->generateSDPDescription();
48 if (sdpDescription == NULL) {
49 // This usually means that a file name that was specified for a
50 // "ServerMediaSubsession" does not exist.
51 snprintf((char*) fResponseBuffer, sizeof fResponseBuffer,
52 "RTSP/1.0 404 File Not Found, Or In Incorrect Format\r\n"
53 "CSeq: %s\r\n"
54 "%s\r\n", cseq, dateHeader());
55 break;
56 }
57 unsigned sdpDescriptionSize = strlen(sdpDescription);
58
59
60 // Also, generate our RTSP URL, for the "Content-Base:" header
61 // (which is necessary to ensure that the correct URL gets used in
62 // subsequent "SETUP" requests).
63 rtspURL = fOurServer.rtspURL(session, fClientInputSocket);
64
65
66 //形成响应DESCRIBE请求的RTSP字符串。
67 snprintf((char*) fResponseBuffer, sizeof fResponseBuffer,
68 "RTSP/1.0 200 OK\r\nCSeq: %s\r\n"
69 "%s"
70 "Content-Base: %s/\r\n"
71 "Content-Type: application/sdp\r\n"
72 "Content-Length: %d\r\n\r\n"
73 "%s", cseq, dateHeader(), rtspURL, sdpDescriptionSize,
74 sdpDescription);
75 } while (0);
76
77
78 delete[] sdpDescription;
79 delete[] rtspURL;
80
81
82 //返回后会被立即发送(没有把socket write操作放入计划任务中)。
83 }
fOurServer.lookupServerMediaSession(urlTotalSuffix)中会在找不到同名ServerMediaSession时新建一个,代表一个RTP流的ServerMediaSession们是被RTSPServer管理的,而不是被RTSPClientSession拥有。为什么呢?因为ServerMediaSession代表的是一个静态的流,也就是可以从它里面获取一个流的各种信息,但不能获取传输状态。不同客户可能连接到同一个流,所以ServerMediaSession应被RTSPServer所拥有。创建一个ServerMediaSession过程值得一观:
1 static ServerMediaSession* createNewSMS(UsageEnvironment& env,char const* fileName, FILE* /*fid*/)
2 {
3 // Use the file name extension to determine the type of "ServerMediaSession":
4 char const* extension = strrchr(fileName, '.');
5 if (extension == NULL)
6 return NULL;
7
8
9 ServerMediaSession* sms = NULL;
10 Boolean const reuseSource = False;
11 if (strcmp(extension, ".aac") == 0) {
12 // Assumed to be an AAC Audio (ADTS format) file:
13 NEW_SMS("AAC Audio");
14 sms->addSubsession(
15 ADTSAudioFileServerMediaSubsession::createNew(env, fileName,
16 reuseSource));
17 } else if (strcmp(extension, ".amr") == 0) {
18 // Assumed to be an AMR Audio file:
19 NEW_SMS("AMR Audio");
20 sms->addSubsession(
21 AMRAudioFileServerMediaSubsession::createNew(env, fileName,
22 reuseSource));
23 } else if (strcmp(extension, ".ac3") == 0) {
24 // Assumed to be an AC-3 Audio file:
25 NEW_SMS("AC-3 Audio");
26 sms->addSubsession(
27 AC3AudioFileServerMediaSubsession::createNew(env, fileName,
28 reuseSource));
29 } else if (strcmp(extension, ".m4e") == 0) {
30 // Assumed to be a MPEG-4 Video Elementary Stream file:
31 NEW_SMS("MPEG-4 Video");
32 sms->addSubsession(
33 MPEG4VideoFileServerMediaSubsession::createNew(env, fileName,
34 reuseSource));
35 } else if (strcmp(extension, ".264") == 0) {
36 // Assumed to be a H.264 Video Elementary Stream file:
37 NEW_SMS("H.264 Video");
38 OutPacketBuffer::maxSize = 100000; // allow for some possibly large H.264 frames
39 sms->addSubsession(
40 H264VideoFileServerMediaSubsession::createNew(env, fileName,
41 reuseSource));
42 } else if (strcmp(extension, ".mp3") == 0) {
43 // Assumed to be a MPEG-1 or 2 Audio file:
44 NEW_SMS("MPEG-1 or 2 Audio");
45 // To stream using 'ADUs' rather than raw MP3 frames, uncomment the following:
46 //#define STREAM_USING_ADUS 1
47 // To also reorder ADUs before streaming, uncomment the following:
48 //#define INTERLEAVE_ADUS 1
49 // (For more information about ADUs and interleaving,
50 // see <http://www.live555.com/rtp-mp3/>)
51 Boolean useADUs = False;
52 Interleaving* interleaving = NULL;
53 #ifdef STREAM_USING_ADUS
54 useADUs = True;
55 #ifdef INTERLEAVE_ADUS
56 unsigned char interleaveCycle[] = {0,2,1,3}; // or choose your own...
57 unsigned const interleaveCycleSize
58 = (sizeof interleaveCycle)/(sizeof (unsigned char));
59 interleaving = new Interleaving(interleaveCycleSize, interleaveCycle);
60 #endif
61 #endif
62 sms->addSubsession(
63 MP3AudioFileServerMediaSubsession::createNew(env, fileName,
64 reuseSource, useADUs, interleaving));
65 } else if (strcmp(extension, ".mpg") == 0) {
66 // Assumed to be a MPEG-1 or 2 Program Stream (audio+video) file:
67 NEW_SMS("MPEG-1 or 2 Program Stream");
68 MPEG1or2FileServerDemux* demux = MPEG1or2FileServerDemux::createNew(env,
69 fileName, reuseSource);
70 sms->addSubsession(demux->newVideoServerMediaSubsession());
71 sms->addSubsession(demux->newAudioServerMediaSubsession());
72 } else if (strcmp(extension, ".ts") == 0) {
73 // Assumed to be a MPEG Transport Stream file:
74 // Use an index file name that's the same as the TS file name, except with ".tsx":
75 unsigned indexFileNameLen = strlen(fileName) + 2; // allow for trailing "x\0"
76 char* indexFileName = new char[indexFileNameLen];
77 sprintf(indexFileName, "%sx", fileName);
78 NEW_SMS("MPEG Transport Stream");
79 sms->addSubsession(
80 MPEG2TransportFileServerMediaSubsession::createNew(env,
81 fileName, indexFileName, reuseSource));
82 delete[] indexFileName;
83 } else if (strcmp(extension, ".wav") == 0) {
84 // Assumed to be a WAV Audio file:
85 NEW_SMS("WAV Audio Stream");
86 // To convert 16-bit PCM data to 8-bit u-law, prior to streaming,
87 // change the following to True:
88 Boolean convertToULaw = False;
89 sms->addSubsession(
90 WAVAudioFileServerMediaSubsession::createNew(env, fileName,
91 reuseSource, convertToULaw));
92 } else if (strcmp(extension, ".dv") == 0) {
93 // Assumed to be a DV Video file
94 // First, make sure that the RTPSinks' buffers will be large enough to handle the huge size of DV frames (as big as 288000).
95 OutPacketBuffer::maxSize = 300000;
96
97
98 NEW_SMS("DV Video");
99 sms->addSubsession(
100 DVVideoFileServerMediaSubsession::createNew(env, fileName,
101 reuseSource));
102 } else if (strcmp(extension, ".mkv") == 0) {
103 // Assumed to be a Matroska file
104 NEW_SMS("Matroska video+audio+(optional)subtitles");
105
106
107 // Create a Matroska file server demultiplexor for the specified file. (We enter the event loop to wait for this to complete.)
108 newMatroskaDemuxWatchVariable = 0;
109 MatroskaFileServerDemux::createNew(env, fileName,
110 onMatroskaDemuxCreation, NULL);
111 env.taskScheduler().doEventLoop(&newMatroskaDemuxWatchVariable);
112
113
114 ServerMediaSubsession* smss;
115 while ((smss = demux->newServerMediaSubsession()) != NULL) {
116 sms->addSubsession(smss);
117 }
118 }
119
120
121 return sms;
122 }
可以看到NEW_SMS("AMR Audio")会创建新的ServerMediaSession,之后马上调用sms->addSubsession()为这个ServerMediaSession添加一个 ServerMediaSubSession 。看起来ServerMediaSession应该可以添加多个ServerMediaSubSession,但这里并没有这样做。如果可以添加多个 ServerMediaSubsession 那么ServerMediaSession与流名字所指定与文件是没有关系的,也就是说它不会操作文件,而文件的操作是放在 ServerMediaSubsession中的。具体应改是在ServerMediaSubsession的sdpLines()函数中打开。
浙公网安备 33010602011771号