Document directory
- Setup command Overview
- 2. Transport Header (1.1)
- 3. Get parameters from subsession (1.2)
The setup command provides an overview of the setup command, which is mainly used to negotiate the communication details between the client and the server, such as the communication protocol and address. The most important part of the setup request is the "transport" header.
The client needs to send a setup command for each stream in the file.
The client can also redirect the RTP data receiving address through the "destination" attribute, but this is supported by the server. In live555, You need to define the macro rtsp_allow_client_destination_setting.
The setup response contains a "session" header, which is a random number generated by the server and used to identify a specific client.
Let's look at a specific setup message instance.
SETUP rtsp://192.168.9.80/123.264/track1 RTSP/1.0CSeq: 31Transport: RTP/AVP/TCP;unicast;interleaved=0-1User-Agent: LibVLC/1.1.0 (LIVE555 Streaming Media v2010.03.16)response: RTSP/1.0 200 OKCSeq: 31Date: Wed, Nov 30 2011 06:40:49 GMTTransport: RTP/AVP/TCP;unicast;destination=192.168.9.80;source=192.168.9.80;interleaved=0-1Session: A00F79DE
Next let's take a look at the specific implementation of the setup command.
1. handle+_setup
Void rtspserver: rtspclientsession: handle+_setup (char const * CSeq, char const * urlpresuffix, char const * urlsuffix, char const * fullrequeststr) {// normally, "urlpresuffix" shocould be the session (Stream) Name, and "urlsuffix" shocould be the subsession (track) Name. // However (being "liberal in what we accept"), we also handle 'aggregate' setup requests (I. E ., without a track name), // in the SP Ecial case where we have only a single track. i. E ., in this case, we also handle: // "urlpresuffix" is empty and "urlsuffix" is the session (Stream) Name, or // "urlpresuffix" concatenated with "urlsuffix" (with "/" inbetween) is the session (Stream) Name. char const * streamname = urlpresuffix; // In the normal case char const * trackID = urlsuffix; // In the normal case char * concatenatedstreamnam E = NULL; // In the normal case do {// find the corresponding session based on the media stream name (File Name). The session is created during the dscribe command processing. // first, make sure the specified stream name exists: fourservermediasession = fourserver. lookupservermediasession (streamname); // The following process the URL without the track ID. When the file contains only one stream, this situation may occur, here, the stream name is saved in the urlsuffix variable if (fourservermediasession = NULL) {// check for the special case (noted above), before we up: If (urlpresuffi X [0] = '\ 0') {streamname = urlsuffix;} else {concatenatedstreamname = new char [strlen (urlpresuffix) + strlen (urlsuffix) + 2]; // allow for the "/" and the trailing '\ 0' sprintf (concatenatedstreamname, "% S/% s", urlpresuffix, urlsuffix); streamname = concatenatedstreamname ;} trackID = NULL; // check again: fourservermediasession = fourserver. lookupservermediasession (streamname); // re-query the session} If (Fourservermediasession = NULL) {handleapps_notfound (CSeq); break;} fourservermediasession-> incrementreferencecount (); // increase the reference count of the session // if this is the first "setup" command processed by this session, you need to build a streamstate array, and preliminary if (fstreamstates = NULL) {// This is the first "setup" for this session. set up our array of States for all of this session's subsessions (tracks): servermediasubsessioniterator ITER (* fourservermediases Sion); For (fnumstreamstates = 0; ITER. Next ()! = NULL; ++ fnumstreamstates) {}// begin by counting the number of subsessions (tracks) 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 "getstreamparamete RS () "call that comes later} // check whether the subsession corresponding to the track ID exists. If no subsession exists, perform error processing. // look up information for the specified subsession (track ): servermediasubsession * subsession = NULL; unsigned streamnum; If (trackID! = NULL & trackID [0]! = '\ 0') {// normal case for (streamnum = 0; streamnum <fnumstreamstates; ++ streamnum) {subsession = fstreamstates [streamnum]. subsession; If (subsession! = NULL & strcmp (trackID, subsession-> trackID () = 0) break;} If (streamnum> = fnumstreamstates) {// The specified track id doesn't exist, so this request fails: handleapps_notfound (CSeq); break ;}} else {// exception: the track ID does not exist in the URL, only when only one subsession is available // weird case: there was no track ID in the URL. // This works only if we have only one subsession: If (fnumstreamstates! = 1) {handle1__bad (CSeq); break;} streamnum = 0; subsession = fstreamstates [streamnum]. subsession;} // assert: subsession! = NULL // process the TRANSPORT header and obtain the transmission information (1.1) // look for a "Transport:" header in the request string, to extract client parameters: streamingmode; char * streamingmodestring = NULL; // set when raw_udp streaming is specified char * handle; u_int8_t clientsdestinationttl; portnumbits listener, listener; unsigned char rtpchannelid, rtcpchannelid; parsetransp Ortheader (fullrequeststr, streamingmode, streamingmodestring, expires, rtpchannelid, rtcpchannelid); If (streamingmode = rtp_tcp & rtpchannelid = 0x= FF | streamingmode! = Rtp_tcp & fclientoutputsocket! = Fclientinputsocket) {// an anomolous situation, caused by a buggy client. either: // 1/tcp streaming was requested, but with no "interleaving =" fields. (QuickTime player sometimes does this .), or // 2/tcp streaming was not requested, but we're doing RTSP-over-HTTP tunneling (which implies TCP streaming ). // In either case, we assume TCP streaming, and set the RTP and RTCP channel IDs to proper Values: streamingmode = rtp_tcp; rtpchannelid = ftcpstreamidcount; rtcpchannelid = ftcpstreamidcount + 1;} ftcpstreamidcount + = 2; port clientrtpport (Protocol); Port clientrtcpport (Protocol ); // process the range header (optional) // next, check whether a "range:" header is present in the request. // This isn' t legal, but some clients do this to combine "setup" and "play": Double rangestart = 0.0, Range End = 0.0; fstreamaftersetup = parserangeheader (fullrequeststr, rangestart, rangeend) | parseplaynowheader (fullrequeststr); // then, get server parameters from the 'subsession ': int tcpsocketnum = streamingmode = rtp_tcp? Fclientoutputsocket:-1; netaddressbits destinationaddress = 0; u_int8_t destinationttl = 255; # ifdef rtsp_allow_client_destination_setting if (clientsdestinationaddressstr! = NULL) {// RTP data is sent to the address specified by destination, rather than the client that is communicating. For security considerations, this function should generally be disabled (remove the macro definition above) // use the client-provided "destination" address. // Note: This potentially allows the server to be used in Denial-of-Service // attacks, so don't enable this code unless you're sure that clients are // trusted. destinationaddress = our_inet_addr (clientsdestinationaddressstr);} // also use the client-provided TTL. destinationttl = clientsdestinationttl; # endif Delete [] clientsdestinationaddressstr; port serverrtpport (0); Port serverrtcpport (0 ); // make sure that we transmit on the same interface that's used by the client (in case we're a multi-homed server): struct sockaddr_in sourceaddr; socklen_t namelen = sizeof sourceaddr; getsockname (comment, (struct sockaddr *) & sourceaddr, & namelen); netaddressbits comment = comment; netaddressbits comment = receivinginterfaceaddr; // note: the following might not work properly, so we ifdef it out for now: # ifdef hack_for_multihomed_servers receivinginterfaceaddr = sendinginterfaceaddr = sourceaddr. sin_addr.s_addr; # endif // obtain the parameter (1.2) from subsession // foursessionid, which identifies the session of a client in rtspserver :: the random number subsession generated by the incomingconnectionhandler function-> getstreamparameters (foursessionid, fclientaddr. clientrtpport, clientrtcpport, tcpsocketnum, rtpchannelid, rtcpchannelid, destinationaddress, destinationttl, fismulticast, serverrtpport, serverrtcpport, fstreamstates [streamnum]. streamtoken); sendinginterfaceaddr = origsendinginterfaceaddr; receivinginterfaceaddr = origreceivinginterfaceaddr; // The following is the Assembly response package ...}
2. Transport Header (1.1)
The transport header contains important information for transmission. View the Transport Header of a setup Request
Transport: RTP/AVP/tcp; unicast; interleaved = 0-1
RTP over TCP transmission and unicast are used. 0 and 1 in the interleaved attribute are used to identify the RTP and RTCP data in the TCP packet respectively.
Next let's take a look at transport's analysis functions.
Static void parsetransportheader (char const * Buf, streamingmode & streamingmode, char * & streamingmodestring, char * & destinationaddressstr, success & destinationttl, portnumbits & expires, // if UDP portnumbits & expires, // if UDP unsigned char & rtpchannelid, // if TCP unsigned char & rtcpchannelid // if TCP) {// initialize the result parameters to default values: streamingmode = r Tp_udp; // by default, RTP is transmitted using UDP... // then, run through each of the fields, looking for ones we handle: Char const * fields = BUF + 11; char * field = strdupsize (fields ); while (sscanf (fields, "% [^;]", field) = 1) {If (strcmp (field, "RTP/AVP/tcp") = 0) {// use RTP over TCP to transmit streamingmode = rtp_tcp;} else if (strcmp (field, "raw/udp ") = 0 | // raw UDP data, without using RTP protocol strcmp (field, "mp2t/h2221/udp") = 0) {// This method has never been seen before. The name should be UDP transmission using a certain protocol, but it is also treated as a raw UDP data streamingmode = raw_udp; streamingmodestring = strdup (field);} else if (_ strncasecmp (field, "Destination =", 12) = 0) {// destination attribute, the client can reset the RTP Sending address through this attribute. Note that the server may reject this attribute Delete [] destinationaddressstr; destinationaddressstr = strdup (field + 12 );} else if (sscanf (field, "TTL % u", & TTL) = 1) {destinationttl = (u_int8_t) TTL;} else if (sscanf (fiel D, "client_port = % hu-% Hu", & P1, & p2) = 2) {// client_port attribute. The client receives the clientrtpportnum = p1 from RTP and RTCP; clientrtcpportnum = P2;} else if (sscanf (field, "client_port = % Hu", & P1) = 1) {// when the client only provides the RTP Port Number, clientrtpportnum = p1; clientrtcpportnum = streamingmode = raw_udp? 0: P1 + 1;} else if (sscanf (field, "interleaved = % u-% u", & rtpcid, & rtcpcid) = 2) {// interleaved attribute, it is only useful when RTP over TCP transmission is used. The two numbers respectively identify the rtpchannelid = (unsigned char) rtpcid of the TCP data packet of RTP and RTCP; // RTP identifies rtcpchannelid = (unsigned char) rtcpcid; // rtcp id} fields + = strlen (field); While (* fields = ';') + fields; // skip over separating '; 'chars if (* fields = '\ 0' | * fields =' \ R' | * fields = '\ n') break ;} delete [] field ;}
3. Get parameters from subsession (1.2)
Getstreamparameters is a pure virtual function defined in the servermediasubsession class. In fact, it is now in the ondemandservermediasubsession subclass. This function will complete the creation of source and rtpsink, and save the ing between them and the client.
Void parameters: getstreamparameters (unsigned clientsessionid, netaddressbits clientaddress, port const & signature, port const & signature, <leftmouse> int values, unsigned char values, unsigned char values, netaddressbits & destinationaddress, u_int8_t &/* destinationttl */, Boolean & ismulticast, Port & serverrtpport, Port & serverrtcpport, void * & streamtoke N) {If (destinationaddress = 0) destinationaddress = clientaddress; struct in_addr destinationaddr; destinationaddr. s_addr = destinationaddress; ismulticast = false; If (flaststreamtoken! = NULL & freusefirstsource) {// when the freusefirstsource parameter is true, you do not need to create instances such as source, sink, and groupsock. You only need to record the client address. // special case: rather than creating a new 'streamstate', // We reuse the one that we 've already created: serverrtpport = (streamstate *) flaststreamtoken)-> serverrtpport (); serverrtcpport = (streamstate *) flaststreamtoken)-> serverrtcpport (); ++ (streamstate *) flaststreamtoken)-> referencecount (); // Add reference records streamtoken = flaststreamtoken;} else {// normally, create a new media source // normal case: Create a new media source: Unsigned streambitrate; // create a source. Do you still remember to create a source when processing the describe command? Yes, it is in the // ondemandservermediasubsession: sdplines () function, but the clientsessionid is 0. // For the specific implementation of the createnewstreamsource function, see the describe command processing process framedsource * mediasource = createnewstreamsource (clientsessionid, streambitrate) in the previous article ); // create 'groupsock 'and 'sink' objects for the destination, // using previously unused server port numbers: rtpsink * rtpsink; using * udpsink; groupsock * rtpgroupsock; groupsock * rtcpgroupsock; portnumbits serverportnum; If (clientrtcpport. num () = 0 ){ // Use raw UDP for transmission. Of course, RTCP is not used. // we're streaming raw UDP (not RTP ). create a single groupsock: noreuse dummy; // ensures that we skip over ports that are already in use for (serverportnum = finitialportnum; ++ serverportnum) {struct in_addr dummyaddr; dummyaddr. s_addr = 0; serverrtpport = serverportnum; rtpgroupsock = new groupsock (envir (), dummyaddr, serverrtpport, 255); If (rtpgroupsock-> socketnum ()> = 0) break; // success} rtcpgroupsock = NULL; rtpsink = NULL; udpsink = basicudpsink: createnew (envir (), rtpgroupsock );} else {// create a groupsocks instance. The port numbers used to transmit RTP, RTCP // RTP, and RTCP are adjacent, and the RTP Port Number is an even number. The initial port finitialportnum = 6970, // This is the default parameter of the ondemandservermediasubsession constructor // normal case: We're streaming RTP (over UDP or TCP ). create a pair of // groupsocks (RTP and RTCP), with adjacent port numbers (RTP Port Number even): noreuse dummy; // ensures that we skip over ports that are already in use for (portnumbits serverportnum = finitialportnum; serverportnum + = 2) {struct in_addr dummyaddr; dumm Yaddr. s_addr = 0; serverrtpport = serverportnum; rtpgroupsock = new groupsock (envir (), dummyaddr, serverrtpport, 255); If (rtpgroupsock-> fig () <0) {Delete rtpgroupsock; continue; // try again} serverrtcpport = serverportnum + 1; rtcpgroupsock = new groupsock (envir (), dummyaddr, serverrtcpport, 255); If (rtcpgroupsock-> socketnum () <0) {Delete rtpgroupsock; Delete rtcpgroupsock; continue; // try Gain} break; // success} // create rtpsink. Similar to source, it has been executed in the describe command, for detailed procedures, see the describe command processing process unsigned char rtppayloadtype = 96 + tracknumber ()-1; // If dynamic rtpsink = createnewrtpsink (rtpgroupsock, rtppayloadtype, mediasource); udpsink = NULL ;} // turn off the destinations for each groupsock. they'll get set later // (unless TCP is used instead): If (rtpgroupsock! = NULL) rtpgroupsock-> removealldestinations (); If (rtcpgroupsock! = NULL) rtcpgroupsock-> removealldestinations (); // reconfigure the socket buffer size for sending RTP if (rtpgroupsock! = NULL) {// try to use a big send buffer for RTP-at least 0.1 second of // specified bandwidth and at least 50 kb unsigned rtpbufsize = streambitrate * 25/2; // 1 kbps * 0.1 s = 12.5 bytes if (rtpbufsize <50*1024) rtpbufsize = 50*1024; then (envir (), rtpgroupsock-> socketnum (), rtpbufsize ); // This function is defined in groupsock} // stream token is set up. Other mappings include sink, source, groupsock, etc. // note, l Ive555 defines two streamstate structures. The streamstate here is defined as a class. In rtspserver, // defines an internal structure streamstate, and its streamtoken Member points to the streamstate instance here // set up the state of the stream. the stream will get started later: streamtoken = flaststreamtoken = new streamstate (* This, serverrtpport, serverrtcpport, rtpsink, udpsink, streambitrate, mediasource, rtpgroupsock, rtcpgroupsock );} // destinations is defined here to save the destination address, RTP Port, and RTCP port, and save it and the corresponding clientsessionid to the hash table // fdestinationshashtable, this hash table is defined in the ondemandservermediasubsession class // record these destinations as being for this client session ID: destinations * destinations; If (tcpsocketnum <0) {// UDP destinations = new destinations (destinationaddr, clientrtpport, clientrtcpport);} else {// TCP destinations = new destinations (tcpsocketnum, rtpchannelid, rtcpchannelid );} fdestinationshashtable-> Add (char const *) clientsessionid, destinations );}