RTSP.cpp

00001 00002 /*********************************************************************** 00003 * * 00004 * ViTooKi * 00005 * * 00006 * title: RTSP.cpp * 00007 * * 00008 * * 00009 * * 00010 * ITEC institute of the University of Klagenfurt (Austria) * 00011 * http://www.itec.uni-klu.ac.at * 00012 * * 00013 * * 00014 * For more information visit the ViTooKi homepage: * 00015 * http://ViTooKi.sourceforge.net * 00016 * vitooki-user@lists.sourceforge.net * 00017 * vitooki-devel@lists.sourceforge.net * 00018 * * 00019 * This file is part of ViTooKi, a free video toolkit. * 00020 * ViTooKi is free software; you can redistribute it and/or * 00021 * modify it under the terms of the GNU General Public License * 00022 * as published by the Free Software Foundation; either version 2 * 00023 * of the License, or (at your option) any later version. * 00024 * * 00025 * This program is distributed in the hope that it will be useful, * 00026 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 00027 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 00028 * GNU General Public License for more details. * 00029 * * 00030 * You should have received a copy of the GNU General Public License * 00031 * along with this program; if not, write to the Free Software * 00032 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, * 00033 * MA 02111-1307, USA. * 00034 * * 00035 ***********************************************************************/ 00036 00037 /*********************************************************************** 00038 * * 00039 * REVISION HISTORY: * 00040 * * 00041 * * 00042 * * 00043 ***********************************************************************/ 00044 00045 #ifndef WINCE 00046 #include <fcntl.h> 00047 #endif 00048 #include <assert.h> 00049 00050 #ifndef WIN32 00051 #include <unistd.h> 00052 #include <sys/socket.h> 00053 #include <netdb.h> 00054 #include <netinet/in.h> 00055 #include <arpa/inet.h> 00056 #include <sys/stat.h> 00057 #endif 00058 00059 #ifdef WIN32 00060 #ifndef WINCE 00061 #include <sys/timeb.h> 00062 #else 00063 #include <time.h> 00064 #endif 00065 #else 00066 #include <sys/time.h> 00067 #endif 00068 00069 #include <time.h> 00070 00071 /* included from common-UCL RTP for IOD parsing */ 00072 #ifdef WINCE 00073 #include "3rdparty/commonucl/base64.h" 00074 #else 00075 #include "base64.h" 00076 #endif 00077 00078 #include "RTSP.hpp" 00079 #include "global.hpp" 00080 #include "DataChannel.hpp" 00081 #include "ContainerInfo.hpp" 00082 #include "DataSink.hpp" 00083 #include "AudioESInfo.hpp" 00084 #include "VideoESInfo.hpp" 00085 #include "BifsESInfo.hpp" 00086 #include "Url.hpp" 00087 #include "SDP.hpp" 00088 #include "PacketizationLayer.hpp" 00089 00090 #include "../metadata/TerminalCapabilities.hpp" 00091 #include "../metadata/MP21.hpp" 00092 00093 #include "../adaptors/MP4Decoder.hpp" 00094 #include "../io/Rtp.hpp" 00095 00096 const char *const 00097 RTSP::allowedCommands = 00098 "OPTIONS, DESCRIBE, SETUP, PLAY, PAUSE, TEARDOWN, GET_PARAMETER, SET_PARAMETER, REDIRECT"; 00099 const int RTSP::SL_PAYLOAD_TYPE = 96; 00100 const int RTSP::TTL = 16; 00101 00102 RTSP::RTSP() { 00103 seqNr = 1; 00104 lastStatus=0; 00105 buffer[0]='\0'; 00106 protoID = PROTO_RTSP; 00107 }; 00108 00109 bool RTSP::applyReqToSession(const char *buffer, u32 bytesRead, 00110 Session * ses) 00111 { 00112 00113 // an RTSP command consists of the following parts: 00114 // $COMMAND $URL RTSP/1.0 00115 // followed by other lines, depending on $COMMAND 00116 // followed by \r\n\r\n 00117 dprintf_full("RTSP::applyReqToSession: Request of size %i\n", 00118 bytesRead); 00119 return handleSingleRequest(ses, buffer, bytesRead); 00120 00121 }; 00122 00123 bool RTSP::applyReqToSession(const char *buffer, u32 bytesRead, 00124 Session * ses, bool optionsCallback) 00125 { 00126 this->optionsCallback = optionsCallback; 00127 return applyReqToSession(buffer, bytesRead, ses); 00128 } 00129 00130 bool RTSP::handleSingleRequest(Session * ses, const char *buffer, 00131 int bytesRead) 00132 { 00133 00134 00135 // parse request: DESCRIBE, SETUP, PLAY, PAUSE, OPTIONS 00136 MSG_Type msgtype = MSG_OPTIONS; 00137 Url *filename = NULL; 00138 int cseq = -1; 00139 const char *remaining=NULL; 00140 dprintf_full("RTSP::handleSingleRequest size %i\n",bytesRead); 00141 if (false == parseRequest(buffer,bytesRead,&msgtype,&filename,&cseq,&remaining)) { 00142 generateBadCMD(); 00143 dprintf_err("RTSP::handleSingleRequest: parse failed, file %s, CSeq %i\r\n", 00144 (filename ? filename->getPath() : "NULL"), cseq); 00145 if(filename) 00146 delete filename; 00147 return false; 00148 } 00149 //dprintf_full("RTSP::handleSingleRequest Remaining is XXXX\n%s\nXXXX\n",remaining); 00150 bool ret = true; 00151 switch (msgtype) { 00152 case MSG_DESCRIBE: 00153 ret=ses->connect(filename,remaining); 00154 break; 00155 case MSG_SETUP: 00156 ret=ses->setup(filename,remaining); 00157 break; 00158 case MSG_PLAY: 00159 ret=ses->play(filename,remaining); 00160 00161 break; 00162 case MSG_PAUSE: 00163 ret=ses->pause(filename,remaining); 00164 break; 00165 case MSG_TEARDOWN: 00166 ret=ses->tearDown(0,true,filename,remaining); 00167 break; 00168 case MSG_OPTIONS: 00169 if (optionsCallback == true) { 00170 ret = ses->options(filename, remaining); 00171 } else { 00172 generateCMDOptionsReply();ret=true; 00173 incSeqNr(); 00174 } 00175 break; 00176 case GET_PARAMETER: 00177 ret= ses->getOptions(filename,remaining); 00178 break; 00179 case SET_PARAMETER: 00180 ret= ses->setOptions(filename,remaining); 00181 break; 00182 case MSG_REDIRECT: 00183 ; 00184 default: 00185 generateCMDnotSupportedReply(); 00186 ret=false; 00187 break; 00188 }; 00189 delete filename; 00190 return ret; 00191 }; 00192 00193 00194 // parses DESCRIBE and returns the player id 00195 bool RTSP::parseCMDDescribe(Session * ses, const Url *fileName, 00196 const char *remaining, char** playerId) 00197 { 00198 00199 00200 dprintf_full("RTSP::parseCMDDescribe: file %s\r\n", fileName->toString()); 00201 const char* lptr=strstr(remaining,"User-Agent:"); 00202 const char* lptrEnd=NULL; 00203 if(!lptr) { 00204 (*playerId)=new char[1]; 00205 (*playerId)[0]='\0'; 00206 return true; // INVALID_HEADER??? 00207 } 00208 lptr+=strlen("User-Agent:"); 00209 while(*lptr==' ') //skip white space 00210 lptr++; 00211 00212 lptrEnd=strstr(lptr,"\n"); // goto end of line 00213 if(!lptrEnd) { 00214 (*playerId)=new char[1]; 00215 (*playerId)[0]='\0'; 00216 return true; // INVALID_HEADER??? 00217 } 00218 lptrEnd--; 00219 while(*lptrEnd=='\r' || *lptrEnd==' ') 00220 --lptrEnd; 00221 // lptrEnd now points to the last char of the player ID 00222 *playerId=new char[lptrEnd-lptr+2]; 00223 strncpy(*playerId,lptr,lptrEnd-lptr+1); 00224 (*playerId)[lptrEnd-lptr+1]='\0'; 00225 return true; 00226 00227 }; 00228 00229 bool RTSP::generateCMDDescribeReply(Session * ses, const Url *fileName, const char *sourceServerName, const char* playerId) { 00230 00231 assert(ses && sourceServerName && fileName); 00232 ContainerInfo *sao = ses->getContainerInfo(); 00233 char *result = 0; 00234 Adaptor* a=ses->getSuggestedAdaptor(); 00235 bool perfectHit=(a==NULL); 00236 if(a) 00237 delete a; 00238 a=NULL; 00239 if (sao) { 00240 printf("RSTP::generateCMDDescribeReply: +++++++++++++++SAO OK\n"); 00241 // FIXME: support UserPreferences 00242 if(ses->getTerminalCapabilities()) { 00243 // enable rtx only with our player 00244 result=SDP::generate(sao, perfectHit,fileName->getPath(), sourceServerName, 00245 SL_PAYLOAD_TYPE, 16, 8, ses->getTerminalCapabilities(), 00246 true, ses->getAllRtxGroups()); 00247 } 00248 else { 00249 // FIXED: correct last two params in SDP!!! -> 16,8 copied from MOH code 00250 result = SDP::generate(sao, perfectHit, fileName->getPath(), sourceServerName, 00251 SL_PAYLOAD_TYPE, 16, 8); 00252 } 00253 } 00254 if (!sao || !result) { 00255 this->generateFileNotFound(); 00256 lastStatus=NOTFOUND; 00257 return false; 00258 } else { 00259 char *pTmp = new char[MSG_BUFFER_SIZE]; 00260 char *next = pTmp; 00261 int cnt = 0; 00262 pTmp[0] = 0; 00263 char timeNow[30]; 00264 00265 getTimeString(timeNow, 30); 00266 00267 cnt = sprintf(next, "Date: %s\r\nExpires: %s\r\n", timeNow, timeNow); 00268 next += cnt; 00269 00270 /* cnt=sprintf(next,"x-Accept-Retransmit: our-retransmit\r\nx-Accept-Dynamic-Rate: 1\r\n"); 00271 next+=cnt; */ 00272 cnt = sprintf(next, "Content-Base: %s\r\n\r\n", fileName->toString()); 00273 next += cnt; 00274 sprintf(buffer, 00275 "RTSP/1.0 200 OK\r\nServer: %s\r\nCSeq: %i\r\n" 00276 "Content-Type: application/sdp\r\n" 00277 // "Cache-Control: must-revalidate\r\n" 00278 "Content-Length: %i\r\n" 00279 "%s" "%s\r\n\r\n", SERVER_ID, seqNr, strlen(result) + 4, pTmp, 00280 result); 00281 lastStatus=OK; 00282 delete [] pTmp; 00283 }; 00284 if (result) 00285 delete result; 00286 return true; 00287 }; 00288 00289 00290 void RTSP::generateNotImplemented() 00291 { 00292 00293 buffer[0]=0; 00294 sprintf(buffer, "RTSP/1.0 501 Not Implemented\r\n\r\n"); 00295 }; 00296 00297 00298 void RTSP::generateBadCMD() 00299 { 00300 00301 buffer[0]=0; 00302 sprintf(buffer, "RTSP/1.0 400 Bad Request\r\nAllow: %s\r\n\r\n", 00303 RTSP::allowedCommands); 00304 }; 00305 00306 void RTSP::generateCMDnotSupportedReply() 00307 { 00308 buffer[0]=0; 00309 sprintf(buffer, 00310 "RTSP/1.0 405 Method Not Allowed\r\nCSeq: %i\r\nAllow: %s\r\n\r\n", 00311 seqNr, allowedCommands); 00312 lastStatus=UNKNOWNCMD; 00313 }; 00314 00315 void RTSP::generateFileNotFound() 00316 { 00317 buffer[0]='\0'; 00318 sprintf(buffer, "RTSP/1.0 404 Not Found\r\nCSeq: %i\r\n\r\n", 00319 seqNr); 00320 lastStatus=NOTFOUND; 00321 00322 }; 00323 00324 void RTSP::generateCMDOptionsReply() 00325 { 00326 buffer[0]=0; 00327 sprintf(buffer, "RTSP/1.0 200 OK\r\nCSeq: %i\r\nPublic: %s\r\n\r\n", 00328 seqNr, allowedCommands); 00329 lastStatus=OK; 00330 }; 00331 00332 void RTSP::generateOk() 00333 { 00334 buffer[0]=0; 00335 sprintf(buffer, "RTSP/1.0 200 OK\r\nCSeq: %i\r\n\r\n",seqNr); 00336 }; 00337 00338 void RTSP::generateSessionNotFound() 00339 { 00340 buffer[0]=0; 00341 sprintf(buffer, 00342 "RTSP/1.0 454 Session Not Found\r\nCSeq: %i\r\n\r\n", 00343 seqNr); 00344 lastStatus=NOTFOUND; 00345 }; 00346 00347 void RTSP::generateInternalServerError() 00348 { 00349 buffer[0]=0; 00350 sprintf(buffer, "RTSP/1.0 500 Internal Server Error\r\nCSeq: %i\r\n\r\n",seqNr); 00351 }; 00352 00353 void RTSP::generateDestUnreachable() 00354 { 00355 buffer[0]=0; 00356 sprintf(buffer, "RTSP/1.0 462 Destination unreachable\r\nCSeq: %i\r\n\r\n",seqNr); 00357 }; 00358 00359 bool RTSP::parseCMDPlay(Session * ses, const Url *filename, 00360 const char *remaining, int* sessionId, double* startTime, double* endTime, double* maxPrefetch) 00361 { 00362 buffer[0]=0; 00363 char *tmp=NULL; 00364 assert(remaining && sessionId && startTime && endTime && maxPrefetch); 00365 // parse remaining to get session key 00366 *sessionId=this->extractSessionKeyFromCMD(remaining); 00367 if((unsigned)*sessionId==INVALID_SESSIONID) { 00368 dprintf_err("RTSP::parseCMDPlay extracted invalid session key\n"); 00369 return false; 00370 } 00371 // search for Range: npt=0.000000-519.960000 00372 *startTime=0.0; 00373 if ( (tmp=strstr(remaining, "Range:")) != NULL && 00374 (tmp=strstr(tmp,"npt="))!= NULL) { 00375 tmp+=4; 00376 dprintf_full("RTSP::parseCMDPlay PARSE String in PLAY: X%sX\r\n",tmp); 00377 if(1!=sscanf(tmp, "%lf",startTime)) { 00378 *startTime=0.0; 00379 dprintf_err("RTSP::parseCMDPlay FAILED to get startTime %lf\r\n",*startTime); 00380 } 00381 if((tmp=strstr(tmp,"-"))!=NULL) { 00382 tmp++; // point after - 00383 if(1!=sscanf(tmp, "%lf",endTime)) { 00384 *endTime=-1.0; 00385 dprintf_err("RTSP::parseCMDPlay FAILED to get endTime %lf\r\n",*endTime); 00386 } 00387 } 00388 00389 } 00390 // x-prebuffer: maxtime=2.000000 00391 00392 if( (tmp=strstr(remaining, "x-prebuffer:")) && 00393 (tmp=strstr(tmp,"maxtime="))) { 00394 sscanf(tmp+strlen("maxtime="),"%lf",maxPrefetch); 00395 } 00396 else 00397 *maxPrefetch=0.0; 00398 dprintf_full("RTSP::parseCMDPlay Session %i start %lf end %lf prefetch %lf\n", 00399 *sessionId,*startTime,*endTime,*maxPrefetch); 00400 return true; 00401 }; 00402 00403 00404 bool RTSP::parseRequest(const char *request, int len, MSG_Type * msgtype, 00405 Url **filename, int *cseq, const char **remaining) 00406 { 00407 dprintf_full("RTSP::parseRequest\r\n"); 00408 *filename=NULL; 00409 *remaining=NULL; 00410 *msgtype=MSG_UNKNOWN; 00411 const char *lptr = request; 00412 const char *lptrEnd=request; 00413 char *cpUrl = new char[MAX_STR_LEN]; 00414 00415 if (0 == strncmp("OPTIONS", request,strlen("OPTIONS"))) { 00416 *msgtype = MSG_OPTIONS; 00417 lptr+=strlen("OPTIONS"); 00418 } 00419 else if (0 == strncmp("DESCRIBE", request,strlen("DESCRIBE"))) { 00420 *msgtype = MSG_DESCRIBE; 00421 lptr+=strlen("DESCRIBE"); 00422 } 00423 else if (0 == strncmp("SETUP", request,strlen("SETUP"))) { 00424 *msgtype = MSG_SETUP; 00425 lptr+=strlen("SETUP"); 00426 } 00427 else if (0 == strncmp("PLAY", request, strlen("PLAY"))) { 00428 *msgtype = MSG_PLAY; 00429 lptr+=strlen("PLAY"); 00430 } 00431 else if (0 == strncmp("PAUSE", request,strlen("PAUSE"))) { 00432 *msgtype = MSG_PAUSE; 00433 lptr+=strlen("PAUSE"); 00434 } 00435 else if (0 == strncmp("TEARDOWN", request,strlen("TEARDOWN"))) { 00436 *msgtype = MSG_TEARDOWN; 00437 lptr+=strlen("TEARDOWN"); 00438 } 00439 else if (0 == strncmp("GET_PARAMETER", request, strlen("GET_PARAMETER"))) { 00440 *msgtype = GET_PARAMETER; 00441 lptr+=strlen("GET_PARAMETER"); 00442 } 00443 else if (0 == strncmp("SET_PARAMETER", request, strlen("SET_PARAMETER"))) { 00444 *msgtype = SET_PARAMETER; 00445 lptr+=strlen("SET_PARAMETER"); 00446 } 00447 while(*lptr==' ') 00448 lptr++; 00449 // lptr now points to first of url 00450 lptrEnd=strstr( lptr,"RTSP/1.0\r\n"); 00451 if(!lptrEnd || lptr>(request+len) || lptrEnd>(request+len) ) { 00452 *msgtype=MSG_UNKNOWN; 00453 dprintf_err("Rtsp::parseRequest: Malformed request\n"); 00454 delete [] cpUrl; 00455 return false; 00456 } 00457 00458 // now set urlEnd to point to the last char of the url 00459 const char* urlEnd=lptrEnd; 00460 urlEnd--; 00461 while(*urlEnd==' ') 00462 urlEnd--; 00463 00464 if(urlEnd<lptr) { 00465 dprintf_err("Rtsp::parseRequest: invalid url\n"); 00466 delete [] cpUrl; 00467 return false; 00468 } 00469 00470 // now position lptrEnd to the next line 00471 while(*lptrEnd!='\n') 00472 lptrEnd++; 00473 // point after \n 00474 lptrEnd++; 00475 00476 int lenStr=urlEnd-lptr+1; 00477 if(lenStr>MAX_STR_LEN-1) 00478 lenStr=MAX_STR_LEN-1; 00479 strncpy(cpUrl,lptr, lenStr); 00480 cpUrl[lenStr]='\0'; 00481 00482 dprintf_full("RTSP::parseRequest: url %s\r\n",cpUrl); 00483 *filename=new Url(cpUrl); 00484 00485 // FIX: OPTIONS only has IP+Port or just a *, no filename! 00486 if (*msgtype == MSG_OPTIONS) { 00487 delete [] cpUrl; 00488 return true; 00489 } 00490 00491 lptr = lptrEnd; 00492 if (! (*filename)->getPath()) { 00493 delete *filename; 00494 *filename=NULL; 00495 dprintf_err("Rtsp::parseRequest: failed to detect fileName\n"); 00496 delete [] cpUrl; 00497 return false; 00498 } 00499 00500 lptr = strstr(lptr, "CSeq: "); 00501 if (!lptr) { 00502 dprintf_err("Rtsp::parseRequest: failed to detect Cseq number\n"); 00503 delete [] cpUrl; 00504 return false; 00505 } 00506 00507 const char *lcseq = (lptr += 6); 00508 while(*lcseq==' ') 00509 lcseq++; 00510 00511 if(1!=sscanf(lcseq,"%i",cseq)) { 00512 delete [] cpUrl; 00513 return false; 00514 } 00515 00516 00517 while (*lptr != '\n') 00518 lptr++; 00519 lptr++; 00520 *remaining = lptr; 00521 if (*remaining <= (request + len)) { 00522 delete [] cpUrl; 00523 return true; 00524 } else { 00525 delete [] cpUrl; 00526 return false; 00527 } 00528 } 00529 00530 00531 int RTSP::generateSessionKey() 00532 { 00533 int next_key=0; 00534 int sesKey=0; 00535 00536 #if (defined(WIN32) && !defined(WINCE)) 00537 struct timeb tv; 00538 ftime(&tv); 00539 srand48(tv.millitm); 00540 00541 #else /* */ 00542 struct timeval tv; 00543 gettimeofday(&tv, 0); 00544 srand48(tv.tv_usec); 00545 #endif /* */ 00546 00547 next_key = lrand48() / 2; // can never generate a number larger than 2 ** 31 00548 sesKey=next_key++; 00549 return sesKey; 00550 }; 00551 00552 bool RTSP::parseCMDSetup(Session * ses, const Url *fileName, 00553 const char * const remaining, bool* hasSessionKey, int* sesKey, 00554 char** protocols, char** unicast,int* esId,int* rPort, int* rPort2, 00555 bool* hasRtxPort, u32* clientRtxPort, char** clientAddress) 00556 { 00557 assert(fileName && remaining && hasSessionKey && sesKey && 00558 protocols && unicast && esId && rPort && rPort2 && hasRtxPort && 00559 clientRtxPort); 00560 00561 buffer[0]=0; 00562 (*protocols)=(*unicast)=NULL; 00563 00564 // Quicktime sends: Transport: RTP/ADV;unicast;client_port=6970-6971 00565 // scan for ; 00566 const char *lptr=NULL; 00567 // create a session identifier 00568 // QT requests audio & video with the same sessionkey 00569 // check if SETUP contains Session: %l 00570 dprintf_full("RTSP::parseCMDSetup:Y%sY\r\n",remaining); 00571 *hasSessionKey=false; 00572 lptr=strstr(remaining,"Session:"); 00573 if(lptr) { 00574 dprintf_full("RTSP::parseCMDSetup: Detected Session key in SETUP\r\n"); 00575 lptr+=strlen("Session:"); 00576 if(1!=sscanf(lptr,"%i",sesKey)) { 00577 dprintf_err("RTSP::parseCMDSetup failed to extract sessionkey\r\n"); 00578 } 00579 else { 00580 *hasSessionKey=true; 00581 } 00582 } 00583 else { 00584 dprintf_full("RTSP::handleCMDSetup: No Session key in SETUP request\r\n"); 00585 } 00586 00587 00588 bool error = false; 00589 const char* filen=fileName->getPath(); 00590 char *l2 = strchr(filen, '='); 00591 if (l2) { 00592 l2++; 00593 error = (1 != sscanf(l2, "%i", esId)); 00594 } else 00595 error = true; 00596 00597 if (error) 00598 dprintf_err("URL didn't trail with \"=esID\""); 00599 00600 lptr=strstr(remaining,"Transport:"); 00601 if(lptr) { 00602 lptr+=strlen("Transport:"); 00603 while(*lptr==' ') 00604 lptr++; 00605 const char* lptrend=strchr(lptr,';'); 00606 if(lptrend) { 00607 *protocols=new char[lptrend-lptr+1]; 00608 strncpy(*protocols,lptr,lptrend-lptr); 00609 (*protocols)[lptrend-lptr]='\0'; 00610 } 00611 else { 00612 dprintf_full("RTSP::parseCMDSetup: missing keyword \"Transport:\" in string XXX%sXXX",remaining); 00613 lptrend=lptr-1; 00614 error=true; 00615 } 00616 00617 // now get unicast 00618 if(strstr(lptr,"unicast")) { 00619 *unicast=new char[strlen("unicast")+1]; 00620 strcpy(*unicast,"unicast"); 00621 } 00622 else if(strstr(lptr,"multicast")) { 00623 *unicast=new char[strlen("multicast")+1]; 00624 strcpy(*unicast,"multicast"); 00625 } 00626 00627 //get destination address 00628 if (clientAddress != NULL && (lptrend=strstr(lptr, "destination=")) != NULL) { 00629 bool destEndFound = true; 00630 const char *destptrend = strstr(lptrend,";"); 00631 if (destptrend == NULL) { 00632 destptrend = strstr(lptrend, "\r\n"); 00633 if (destptrend == NULL) { 00634 destptrend = strstr(lptrend, "\n"); 00635 if (destptrend == NULL) { 00636 destEndFound = false; 00637 dprintf_full("RTSP::parseCMDSetup: cannot find end of destination value!"); 00638 } 00639 } 00640 } 00641 00642 if (destEndFound) { 00643 uint destlen = destptrend - (lptrend + strlen("destination=")) - 1; 00644 *clientAddress = new char[destlen + 1]; 00645 int i = 0; 00646 const char *myptr = lptrend + strlen("destination="); 00647 while (myptr != NULL && (*myptr) != ';' && (*myptr) != '\r' && (*myptr) != '\n') { 00648 (*clientAddress)[i++] = *(myptr++); 00649 } 00650 (*clientAddress)[i] = '\0'; 00651 dprintf_full("\nclientAddress = %s\r\n", *clientAddress); 00652 } 00653 } else { 00654 dprintf_full("destination parameter not found\n"); 00655 } 00656 00657 // now get client port 00658 lptrend=strstr(lptr,"client_port="); 00659 if(lptrend && !error) { 00660 error = (2 != sscanf(lptrend, "client_port=%i-%i", rPort, rPort2)); 00661 00662 } 00663 else { 00664 *rPort=*rPort2=0; 00665 error=true; 00666 } 00667 lptrend=strstr(lptrend,"client_rtx_port="); 00668 if(lptrend) { 00669 lptrend+=strlen("client_rtx_port="); 00670 // points after the = 00671 *hasRtxPort= (1==sscanf(lptrend,"%u",clientRtxPort)); 00672 } 00673 } 00674 00675 // end parsing 00676 dprintf_full 00677 ("RTSP::parseCMDSetup: protocols:>%s< unicast:>%s<ports: %i %i rtxport %u\r\n", 00678 *protocols, *unicast, *rPort, *rPort2, *clientRtxPort); 00679 return !error; 00680 }; 00681 00682 void RTSP::generateIllegalTransport() 00683 { 00684 buffer[0]='\0'; 00685 sprintf(buffer, 00686 "RTSP/1.0 461 Unsupported Transport\r\nCSeq: %i\r\n\r\n", 00687 seqNr); 00688 lastStatus=ILLEGAL; 00689 } 00690 00691 bool RTSP::isSupportedTransport(const char* protocols) 00692 { 00693 if (strstr(protocols, "TCP") != NULL) 00694 return false; 00695 00696 return true; 00697 }; 00698 00699 bool RTSP::isCmdOk(const char* buf) 00700 { 00701 if( strstr(buf,"RTSP/1.0") && 00702 strstr(buf,"200") && 00703 strstr(buf,"OK")) 00704 return true; 00705 return false; 00706 }; 00707 00708 bool RTSP::generateSetupReply(Session * ses, const Url *fileName, 00709 const int sesKey, const char* protocols, const char* unicast, const char* sourceAddress, 00710 const int esId, const int rPort, const int rPort2, const int lPort, 00711 const u32 clientRtxPort, const u32 serverRtxPort) 00712 { 00713 dprintf_full("RTSP::generateSetupReply: Session* %p, const Url* %s,\ 00714 sesKey %i, protocols %s, unicast %s, sourceAddress %s,\ 00715 esId %i, rPort %i, rPort2 %i, lPort %i,\ 00716 clientRtxPort %u, serverRtxPort %u\n", 00717 ses,fileName->toString(),sesKey,protocols,unicast,sourceAddress,esId,rPort,rPort2, 00718 lPort,clientRtxPort,serverRtxPort); 00719 assert(ses && fileName && protocols && sesKey>0 && unicast && 00720 rPort>0 && rPort2>0 && lPort>0 && esId>=0); 00721 00722 00723 00724 // first check if a setup was already done 00725 // if yes, we do not allow changing ports on the fly 00726 DataChannel * dc = ses->getDataChannel(esId); 00727 00728 if (dc) { 00729 sprintf(buffer, 00730 "RTSP/1.0 455 Method Not Valid In This State\r\nCSeq: %i\r\n\r\n", 00731 seqNr); 00732 lastStatus=ILLEGAL; 00733 // ses->sendResponse(buffer, strlen(buffer)); 00734 return false; 00735 } 00736 00737 00738 // FIXME: not sure, but it could be that QuickTime tries to establish a connection to the RTCP port 00739 // immediately? Don't think so but try it 00740 // prot->open(); 00741 // char *ssrc = prot->getSSRC(); 00742 00743 // build the response 00744 // S->C: RTSP/1.0 200 OK 00745 // CSeq: 302 00746 // Date: 23 Jan 1997 15:35:06 GMT 00747 // Session: 47112344 00748 // Transport: RTP/AVP;unicast; 00749 // client_port=4588-4589;server_port=6256-6257 00750 char timeStr[30]; 00751 00752 00753 int offset=0; 00754 00755 if (getTimeString(timeStr, 30)) { 00756 00757 // delete lt; NOT allowed to do that, statically stored internally in localtime 00758 dprintf_full("RTSP::handleCMDSetup: timeString: %s\r\n", 00759 timeStr); 00760 offset= sprintf(buffer, 00761 "RTSP/1.0 200 OK\r\nCSeq: %i\r\nServer: %s\r\n" 00762 "Last-Modified: %s\r\nCache-Control: must-revalidate\r\n" 00763 "Session: %i\r\n" "Date: %s\r\nExpires: %s\r\n" 00764 // "Transport: %s;%s;client_port=%i-%i;source=%s;server_port=%i-%i", 00765 "Transport: %s;%s;client_port=%i-%i;server_port=%i-%i", 00766 seqNr, SERVER_ID, timeStr, sesKey, timeStr, timeStr, protocols, 00767 unicast, rPort, rPort2, /*sourceAddress,*/ lPort, lPort + 1); 00768 00769 } else { 00770 dprintf_small("RTSP::handleCMDSetup: no time set\r\n"); 00771 offset=sprintf(buffer, "RTSP/1.0 200 OK\r\nCSeq: %i\r\n" 00772 "Last-Modified: %s\r\nCache-Control: must-revalidate\r\n" 00773 "Session: %i\r\n" 00774 // "Transport: %s;%s;client_port=%i-%i;source=%s;server_port=%i-%i", 00775 "Transport: %s;%s;client_port=%i-%i;server_port=%i-%i", 00776 seqNr, timeStr, sesKey, protocols, unicast, rPort, 00777 rPort2, /*sourceAddress,*/ lPort, lPort + 1); 00778 } 00779 00780 if(clientRtxPort>0 && serverRtxPort>0) { 00781 sprintf( (buffer+offset), 00782 ";client_rtx_port=%u;server_rtx_port=%u\r\n\r\n", 00783 clientRtxPort, 00784 serverRtxPort); 00785 } 00786 else { 00787 sprintf( (buffer+offset), "\r\n\r\n"); 00788 } 00789 lastStatus=OK; 00790 00791 return true; 00792 }; 00793 00795 int RTSP::extractSessionKeyFromCMD(const char* remaining) 00796 { 00797 char *tmp=NULL; 00798 int key; 00799 if ( ((tmp=strstr(remaining, "Session:")) == NULL) || 00800 (sscanf(tmp,"%*s %i",&key) != 1) ) { 00801 dprintf_err("Session Tag %i [%s] not found\n",key,tmp); 00802 key=INVALID_SESSIONID; 00803 } 00804 return key; 00805 }; 00806 00807 /* 00808 void RTSP::handleCMDPause(Session * ses, const Url *fileName, 00809 const char *remaining) 00810 { 00811 00812 00813 }; 00814 */ 00815 00816 ContainerInfo * RTSP::parseResponseDescribe(const char *response, const char *url, char** serverName, 00817 list<rtx_group*> *groups) 00818 { 00819 assert( response && url && serverName); 00820 // check if it is really an ok message 00821 if(!strstr(response,"RTSP/1.0 200 OK")) 00822 return NULL; 00823 00824 const char* serverBegin=strstr(response,"Server:"); 00825 if(serverBegin) { 00826 serverBegin+=strlen("Server:"); 00827 while(*serverBegin==' ') 00828 serverBegin++; 00829 const char* serverEnd=strstr(serverBegin,"\r\n"); 00830 if(serverEnd) { 00831 *serverName=new char[serverEnd-serverBegin+1]; 00832 strncpy(*serverName,serverBegin,serverEnd-serverBegin); 00833 (*serverName)[serverEnd-serverBegin]='\0'; 00834 dprintf_full("RTSP::parseResponseDescribe extracted serverName %s\n",*serverName); 00835 } 00836 else 00837 *serverName=NULL; 00838 } 00839 else 00840 *serverName=NULL; 00841 00842 ContainerInfo * mp4Info = new ContainerInfo(url, NULL, ContainerInfo::SDP); 00843 00844 // search rtx_groups 00845 if(groups) { 00846 const char* grp=response; 00847 while( (grp=strstr(grp,"a=group:FID"))!=NULL ) { 00848 rtx_group* group=new rtx_group(NACK,0); 00849 grp+=strlen("a=group:FID"); 00850 if(2!=sscanf( grp, 00851 "%u %u", 00852 &(group->midESInfo), &(group->rtx->mId))) { 00853 delete group; 00854 } 00855 else { 00856 dprintf_full("RTSP::parseResponseDescribe found rtx group: mid %u midES %u\n", 00857 group->rtx->mId,group->midESInfo); 00858 groups->push_back(group); 00859 } 00860 } 00861 } 00862 // we have created the groups 00863 00864 // extract iod 00865 const char *iod = strstr(response, "mpeg4-iod:"); 00866 iod = strstr(iod, "base64,"); 00867 if (iod) { 00868 iod += 7; // points to first char of iod 00869 // iod ends with ", there shouldn't be any space within the IOD 00870 // but there can be a \n! 00871 const char *temp2 = strchr(iod, '"'); 00872 int iodSizeEncoded = temp2 - iod; 00873 u8 iodHandle[MAX_IOD_SIZE]; 00874 00875 //decode 00876 // int iodenclen = str_base64((char*)iod, iodSizeEncoded, iodHandle, MAX_IOD_SIZE, STR_BASE64_DECODE); 00877 int iodenclen = base64decode((unsigned char*)iod, iodSizeEncoded, iodHandle, MAX_IOD_SIZE); 00878 00879 if (iodenclen <= 0) { //wrong or no IOD 00880 dprintf_err("RTSP::parseResponseDescribe: there was a wrong/empty IOD given... ignoring...\n"); 00881 } else 00882 // set decoded IOD 00883 mp4Info->setIODHandle(iodHandle, iodenclen); 00884 } 00885 // search for a=range:npt=0- 66.47000 00886 double durSeconds=0; 00887 00888 const char* temp2=strstr(response,"range:"); 00889 if(temp2) { 00890 temp2=strstr(temp2,"-"); 00891 if(temp2) { 00892 temp2++; 00893 sscanf(temp2,"%lf",&durSeconds); 00894 } 00895 } 00896 const char* media=NULL; 00897 const char* endOfMedia=NULL; 00898 const char* copy=response; 00899 00900 // search media blocks 00901 // media blocks begin with m= 00902 while ((media = strstr(copy, "\nm=")) != NULL) { 00903 00904 //media now points to first m= 00905 media += 3; 00906 copy = media; 00907 endOfMedia = strstr(copy, "\nm="); 00908 if (!endOfMedia) { 00909 // set to end of sdp desc. 00910 endOfMedia = media + strlen(media); 00911 } 00912 00913 // we can have two different media blocks 00914 // one normal one which describes the media 00915 // or one rtx transmission block 00916 // detect this by searching "rtx" 00917 const char* pRtx=strstr(media,"rtx"); 00918 if(pRtx > media && pRtx < endOfMedia) { 00919 // we have a rtx entry 00920 // should we ignore it ? 00921 if(groups) 00922 parseSDP_RTXBlock(media,endOfMedia,groups); 00923 } 00924 else { 00925 ESInfo* es=parseSDPMediaBlock(media,endOfMedia, 00926 durSeconds,mp4Info,groups); 00927 if(es) { 00928 // add es to mp4info 00929 es->enableFrameStatistic(); 00930 mp4Info->addESInfo(es); 00931 } 00932 else { 00933 delete mp4Info; 00934 return NULL; 00935 } 00936 } 00937 } 00938 00939 return mp4Info; 00940 }; 00941 00942 void RTSP::parseSDP_RTXBlock(const char* media, 00943 const char* endOfMedia, 00944 list<rtx_group*> *groups) 00945 { 00946 //m=video 0 RTP/AVPF 99 00947 //a=rtpmap:99 rtx/32000 00948 //a=fmtp:99 apt=98;rtx-time=3000 00949 //a=mid:4 00950 00951 if(!groups) 00952 return; 00953 if(groups->empty()) 00954 return; 00955 00956 // we extract mid, rtx_time, ticks and payload 00957 u32 mid=0; 00958 u32 rtxDelay=0; 00959 u32 ticks=0; 00960 u32 payload=0; 00961 00962 const char* pMedia=strstr(media,"a=rtpmap:"); 00963 if(pMedia>media && pMedia<endOfMedia) { 00964 pMedia+=strlen("a=rtpmap:"); 00965 if(2!=sscanf(pMedia,"%u rtx/%u",&payload,&ticks)) { 00966 dprintf_err("RTSP::parseSDP_RTXBlock failed (ticks)\r\n"); 00967 return; 00968 } 00969 } 00970 pMedia=strstr(media,"rtx-time="); 00971 if(pMedia>media && pMedia<endOfMedia) { 00972 pMedia+=strlen("rtx-time="); 00973 if(1!=sscanf(pMedia,"%u",&rtxDelay)) { 00974 dprintf_err("RTSP::parseSDP_RTXBlock err in rtxDelay\r\n"); 00975 return; 00976 } 00977 } 00978 pMedia=strstr(media,"a=mid:"); 00979 if(pMedia>media && pMedia<endOfMedia) { 00980 pMedia+=strlen("a=mid:"); 00981 if(1!=sscanf(pMedia,"%u",&mid)) { 00982 dprintf_err("RTSP::parseSDP_RTXBlock err in mid\r\n"); 00983 return; 00984 } 00985 } 00986 // got all that is necessary 00987 00988 }; 00989 00990 ESInfo* RTSP::parseSDPMediaBlock( const char* media, 00991 const char* endOfMedia, 00992 const double durSeconds, 00993 ContainerInfo * mp4Info, 00994 list<rtx_group*> *groups) 00995 { 00996 /* 00997 m=video 0 RTP/AVP 96 00998 b=AS:1067 00999 a=rtpmap:96 MP4V-ES/90000 01000 a=control:trackID=1 01001 a=cliprect:0,0,352,288 01002 a=fmtp:96 profile-level-id=1;config=000001010000012000C888800F50B042414183 01003 a=mpeg4-esid:1 01004 a=aspect-ratio=1.3333 01005 */ 01006 01007 const char *temp = NULL; 01008 const char *copy = media; 01009 // normal media block 01010 // now determine type of stream 01011 // Example: m=video 0 RTP/AVP 96 01012 // m=audio 0 RTP/AVP 97 01013 // m=application 0 RTP/AVP 98 01014 // %s %i %s %i 01015 u32 handlerType = 0; 01016 char *handlerString = new char[50]; 01017 char *transportString = new char[40]; 01018 int rtpPayload = -1; 01019 int avg_bw = -1; 01020 int dummy = -1;; 01021 int trackId=-1; 01022 bool err=false; 01023 handlerString[0] = 0; 01024 transportString[0] = 0; 01025 sscanf(copy, "%s %i %s %i", handlerString, &dummy, 01026 transportString, &rtpPayload); 01027 dprintf_full("RTSP::parseSDPMediaBlock handler is %s\n",handlerString); 01028 if (strcmp(handlerString, "video") == 0) { 01029 handlerType = MP4VisualHandlerType; 01030 } 01031 else if (strcmp(handlerString, "audio") == 0) { 01032 handlerType = MP4AudioHandlerType; 01033 } 01034 else if (strcmp(handlerString, "application") == 0) { 01035 // PROBLEM: SDP doesn't make a difference between BIFS or OD 01036 // most servers don't even describe these two streams in SDP 01037 // FIXME: 01038 handlerType = MP4SceneDescriptionHandlerType; 01039 } 01040 01041 const char *pBW = strstr(media,"b=AS:"); 01042 sscanf(pBW+strlen("b=AS:"), "%i",&avg_bw); 01043 avg_bw *= 1024; // bit/sec 01044 01045 // we ignore payload & transport for now 01046 01047 // now search for possible rtx feedback 01048 // a=rtcp-fb:%i nack 01049 // a=mid:3 01050 u32 mid=0; 01051 if(groups && !groups->empty()) { 01052 const char * pMid=strstr(media,"a=mid:"); 01053 char ackType[20]; 01054 ackType[0]=0; 01055 01056 if(pMid>media && pMid<endOfMedia) { 01057 // a valid entry 01058 sscanf( (pMid+strlen("a=mid:")),"%u",&mid); 01059 } 01060 pMid=strstr(media,"a=rtcp-fb:"); 01061 if(pMid) { 01062 pMid+=strlen("a=rtcp-fb:"); 01063 // skip over the number, read ACK-Type 01064 sscanf(pMid,"%*s %s",ackType); 01065 } 01066 if(mid>0 && strlen(ackType)>0) { 01067 // search the group 01068 list<rtx_group*>::iterator li=groups->begin(); 01069 bool stop=false; 01070 while(li!=groups->end() && !stop) { 01071 if( (*li)->midESInfo==mid) { 01072 // found the group, now set the state 01073 for(int r=0;r<(int)LAST_RTX_STATE && !stop;r++) { 01074 if(strcmp(sRTXState[r],ackType)==0) { 01075 (*li)->rtx->state=(RTXState)r; 01076 stop=true; 01077 } 01078 } 01079 } 01080 ++li; 01081 } 01082 } 01083 } 01084 01085 // now get ticks: a=rtpmap:96 MP4V-ES/90000 01086 u32 ticksPerSec=90000; //default 01087 temp=strstr(media,"rtpmap:"); 01088 if(temp != NULL && temp<endOfMedia) { 01089 //search for / 01090 temp=strstr(temp,"/"); 01091 if(temp) { 01092 temp++; 01093 if(1!=sscanf(temp,"%ui",&ticksPerSec)) 01094 ticksPerSec=90000; 01095 } 01096 } 01097 01098 u64 durTicks=(u64) (durSeconds*ticksPerSec); 01099 01100 //now extract streamid 01101 // Example: a=control:trackId=3 01102 temp = strstr(media, "control:"); 01103 trackId=-1; 01104 if(temp != NULL && temp<endOfMedia) { 01105 temp=strstr(temp,"="); 01106 01107 if(temp) { 01108 temp++; 01109 if(1!=sscanf(temp,"%i",&trackId)) 01110 trackId=-1; 01111 } 01112 } 01113 if(trackId<0) 01114 err=true; 01115 01116 /* Extract Rest: 01117 a=cliprect:0,0,352,288 //optional 01118 a=fmtp:96 profile-level-id=1;config=000001010000012000C888800F50B042414183 01119 */ 01120 int width=0; 01121 int height=0; 01122 temp=strstr(media,"cliprect:"); 01123 if(temp!=NULL && temp<endOfMedia) { 01124 temp+=strlen("cliprect:"); 01125 sscanf(temp,"%*i,%*i,%i,%i",&width,&height); 01126 } 01127 // now get the config 01128 u8* encodedDecConf=NULL; 01129 temp=strstr(media,"config="); 01130 if(temp!=NULL && temp<endOfMedia) { 01131 temp+=strlen("config="); 01132 char* tmp2=strstr(temp,"\r"); 01133 if(tmp2==NULL) 01134 tmp2=strstr(temp,"\n"); 01135 01136 encodedDecConf=new u8[(tmp2-temp)+1]; 01137 01138 strncpy((char*)encodedDecConf,temp,(tmp2-temp)); 01139 encodedDecConf[(tmp2-temp)]=0; 01140 } 01141 01142 if(encodedDecConf==NULL) 01143 err=true; 01144 if(err) { 01145 delete [] handlerString; 01146 delete [] transportString; 01147 return NULL; 01148 } 01149 01150 // now get the aspect ratio 01151 float ratio=1.0; 01152 temp=strstr(media,"aspect-ratio="); 01153 if(temp!=NULL && temp<endOfMedia) { 01154 temp+=strlen("aspect-ratio="); 01155 sscanf(temp,"%f",&ratio); 01156 dprintf_full("RTSP::parseSDPMediaBlock got aspect ratio %6.4f\n",ratio); 01157 } 01158 // get frame pattern 01159 char fpTemp[32]; 01160 bool staticFramePattern=true; 01161 u32 gopSize=50; 01162 u32 numBFrames=0; 01163 temp=strstr(media,"frame-pattern="); 01164 if(temp!=NULL && temp<endOfMedia) { 01165 temp+=strlen("frame-pattern="); 01166 sscanf(temp,"%31s",fpTemp); 01167 if(strstr(temp,"dynamic")) 01168 staticFramePattern=false; 01169 } 01170 float bfrmamount=0.0f; 01171 temp=strstr(media,"bframesamount="); 01172 if(temp!=NULL && temp<endOfMedia) { 01173 temp+=strlen("bframesamount="); 01174 if(1!=sscanf(temp,"%f",&bfrmamount)) 01175 bfrmamount=0.2f; 01176 01177 } 01178 if(staticFramePattern) { 01179 // extract num-bframes 01180 temp=strstr(media,"num-bframes"); 01181 if(temp!=NULL && temp<endOfMedia) { 01182 temp+=strlen("num-bframes"); 01183 sscanf(temp," %u",&numBFrames); 01184 } 01185 01186 // extract gop-size 01187 temp=strstr(media,"gop-size"); 01188 if(temp!=NULL && temp<endOfMedia) { 01189 temp+=strlen("gop-size"); 01190 if(1!=sscanf(temp," %u",&gopSize)) 01191 gopSize=50; 01192 } 01193 } 01194 dprintf_full("RTSP::parseSDPMediaBlock got %s frame pattern, gopsize: %u, bframes: %u\n", 01195 staticFramePattern?"STATIC":"DYNAMIC",gopSize,numBFrames); 01196 /* VideoESInfo(u32 streamId, u32 streamtype, ContainerInfo * vob, u32 timeIncrement, 01197 u8 * encodeddecconf, u32 objtype, u32 bufsize, 01198 u32 avgBW, u32 maxBW, u32 timeScale, 01199 u64 duration, u32 size, 01200 u32 w, u32 h, bool colorVideo, 01201 bool complete=true, 01202 CodecID codec_id = CODEC_ID_MPEG4, float quality = 1.0, 01203 float aspect_ratio = 352/288, u32 GOP_size = 50, 01204 u32 num_B_frames = 0); 01205 */ 01206 // FIXME: we hardcode to 3000 ticks!!!!!! we fix that in IO (HACK) 01207 // FIXME: we hardcode 100000 as size... 01208 ESInfo* es=NULL; 01209 if(avg_bw<=0 && (es->isVisualStream() || es->isAudioStream())) { 01210 // FIXME: assume 256000 bit 01211 if(es->isVisualStream()) 01212 avg_bw=256000; 01213 else if(es->isAudioStream()) 01214 avg_bw=128000; 01215 dprintf_err("RTSP.parseSDPMediaBlock: No avg Bandwidth found\n"); 01216 } 01217 if(handlerType==MP4VisualHandlerType) { 01218 es=new VideoESInfo(trackId, 0, 01219 mp4Info,3000, encodedDecConf,0,0, 01220 avg_bw, avg_bw, ticksPerSec, 01221 durTicks,100000,width,height,true,false, 01222 CODEC_ID_MPEG4,1.0, //default values 01223 ratio,staticFramePattern,gopSize,numBFrames,bfrmamount); 01224 } 01225 else if(handlerType==MP4AudioHandlerType) { 01226 // FIXME: we hardcode to 48000hz and 2 channels 01227 es=new AudioESInfo(trackId, 0, 01228 mp4Info,3000, encodedDecConf,0,0, 01229 avg_bw, avg_bw, ticksPerSec, 01230 durTicks,100000,48000,2,false); 01231 } 01232 else if(handlerType==MP4SceneDescriptionHandlerType) { 01233 es=new BifsESInfo(trackId, 0, 01234 mp4Info,3000, encodedDecConf,0,0, 01235 avg_bw, avg_bw, ticksPerSec, 01236 durTicks,10000,false); 01237 } 01238 01239 // now link the ES into the rtx_group 01240 if(groups && !groups->empty()) { 01241 list<rtx_group*>::iterator gi=groups->begin(); 01242 while(gi!=groups->end()) { 01243 01244 if( (*gi)->midESInfo==mid) { 01245 (*gi)->es=es; 01246 gi=groups->end(); 01247 } 01248 else 01249 ++gi; 01250 } 01251 } 01252 01253 delete [] handlerString; 01254 delete [] transportString; 01255 return es; 01256 }; 01257 01258 /* 01259 DESCRIBE rtsp://143.205.122.43:80/sample_300kbit.mp4 RTSP/1.0 01260 CSeq: 1 01261 Accept: application/sdp 01262 User-Agent: QTS (qtver=6.0;os=Windows NT 5.0Service Pack 2) 01263 */ 01264 void RTSP::generateDescribeForUrl(const char* url, const char* playerID, const TerminalCapabilities* tc,const UserPreferences* up) { 01265 assert(url); 01266 int bufferOffset=0; 01267 buffer[0]=0; 01268 bufferOffset = sprintf(buffer,"DESCRIBE %s RTSP/1.0\r\nCSeq: %i\r\n" 01269 "Accept: application/sdp\r\n" 01270 "User-Agent: %s\r\n" 01271 ,url,seqNr,playerID); 01272 01273 char* tcd=NULL; 01274 if(up) { 01275 char* upd=MP21::generateUserPreferencesDescription(up); 01276 int updlength=strlen(upd)+4; 01277 bufferOffset += sprintf(buffer+bufferOffset, 01278 "Content-Type: application/mpeg21_dia\r\n" 01279 "Content-Length: %i\r\n%s\r\n\r\n",updlength,upd); 01280 } 01281 else if(tc) { 01282 tcd=MP21::generateTerminalCapabilityDescription(tc); 01283 int tcdLength=strlen(tcd)+4; 01284 bufferOffset += sprintf(buffer+bufferOffset, 01285 "Content-Type: application/mpeg21_dia\r\n" 01286 "Content-Length: %i\r\n%s\r\n\r\n",tcdLength,tcd); 01287 } 01288 else 01289 sprintf(buffer+bufferOffset,"\r\n"); 01290 }; 01291 01292 /* 01293 SETUP rtsp://143.205.122.43:80/sample_300kbit.mp4/trackID=3 RTSP/1.0 01294 CSeq: 2 01295 Transport: RTP/AVP;unicast;client_port=6970-6971;client_rtx_port=6972 01296 */ 01297 void RTSP::generateSetup(const char* url, int trackId, int clientPort,bool unicast,u32 rtxPort,int sessionId) 01298 { 01299 assert(url && trackId>=0 && clientPort>0); 01300 buffer[0]=0; 01301 char* mcast[]={"multicast","unicast"}; 01302 01303 if(rtxPort==0) { 01304 if(sessionId<=0) { 01305 sprintf(buffer,"SETUP %s/trackID=%i RTSP/1.0\r\n" 01306 "CSeq: %i\r\n" 01307 "Transport: RTP/UDP;%s;client_port=%i-%i\r\n\r\n", 01308 url,trackId, 01309 seqNr, 01310 mcast[unicast ? 1 :0],clientPort,clientPort+1); 01311 } 01312 else { 01313 sprintf(buffer,"SETUP %s/trackID=%i RTSP/1.0\r\n" 01314 "CSeq: %i\r\n" 01315 "Session: %i\r\n" 01316 "Transport: RTP/UDP;%s;client_port=%i-%i\r\n\r\n", 01317 url,trackId, 01318 sessionId, 01319 seqNr, 01320 mcast[unicast ? 1 :0],clientPort,clientPort+1); 01321 } 01322 } 01323 else { 01324 if(sessionId<=0) { 01325 sprintf(buffer,"SETUP %s/trackID=%i RTSP/1.0\r\n" 01326 "CSeq: %i\r\n" 01327 "Transport: RTP/UDP;%s;client_port=%i-%i;client_rtx_port=%u\r\n\r\n", 01328 url,trackId, 01329 seqNr, 01330 mcast[unicast ? 1 :0],clientPort,clientPort+1,rtxPort); 01331 } 01332 else { 01333 sprintf(buffer,"SETUP %s/trackID=%i RTSP/1.0\r\n" 01334 "CSeq: %i\r\n" 01335 "Session: %i\r\n" 01336 "Transport: RTP/UDP;%s;client_port=%i-%i;client_rtx_port=%u\r\n\r\n", 01337 url,trackId, 01338 sessionId, 01339 seqNr, 01340 mcast[unicast ? 1 :0],clientPort,clientPort+1,rtxPort); 01341 } 01342 } 01343 }; 01344 01345 void RTSP::generatePlay(const char* url, u32 session, double start, double nptEnd, double maxPrefetch) { 01346 assert(url); 01347 buffer[0]=0; 01348 01349 sprintf(buffer, "PLAY %s RTSP/1.0\r\n" 01350 "CSeq: %i\r\n" 01351 "Session: %i\r\n" 01352 "x-prebuffer: maxtime=%lf\r\n" 01353 "Range: npt=%lf-%lf\r\n\r\n", 01354 url,seqNr,session,maxPrefetch,start,nptEnd); 01355 dprintf_full("RTSP::generatePlay: %s\n",buffer); 01356 }; 01357 01358 /* 01359 PAUSE rtsp://143.205.122.43:80/sample_300kbit.mp4 RTSP/1.0 01360 CSeq: 4 01361 Session: 12345678 01362 */ 01363 void RTSP::generatePause(const char* url, u32 session) { 01364 assert(url); 01365 buffer[0]=0; 01366 sprintf(buffer, "PAUSE %s RTSP/1.0\r\n" 01367 "CSeq: %i\r\n" 01368 "Session: %u\r\n\r\n", 01369 url,seqNr,session); 01370 }; 01371 01372 /* 01373 TEARDOWN rtsp://143.205.122.43:80/sample_300kbit.mp4 RTSP/1.0 01374 CSeq: 5 01375 Session: 12345678 01376 */ 01377 void RTSP::generateTeardown(const char* url, u32 session) { 01378 assert(url); 01379 buffer[0]=0; 01380 sprintf(buffer, "TEARDOWN %s RTSP/1.0\r\n" 01381 "CSeq: %i\r\n" 01382 "Session: %u\r\n\r\n", 01383 url,seqNr,session); 01384 }; 01385 01386 /* 01387 RTSP/1.0 200 OK 01388 Server: DSS/4.1.1 (Build/412.36; Platform/Win32) 01389 CSeq: 2 01390 Last-Modified: Fri, 05 Jul 2002 15:20:14 GMT 01391 Cache-Control: must-revalidate 01392 Session: 53034256198111 01393 Date: Mon, 09 Sep 2002 13:04:48 GMT 01394 Expires: Mon, 09 Sep 2002 13:04:48 GMT 01395 Transport: RTP/AVP;unicast;client_port=6970-6971;source=143.205.122.43;server_port=6970-6971;ssrc=00002908 01396 */ 01397 int RTSP::parseResponseSetup(const char* buffer, int *sessionID, int *clientPort, 01398 int *serverPort,bool *unicast, char** ssrc, 01399 rtx_info* info) { 01400 01401 if(!strstr(buffer,"RTSP/1.0 200 OK")) 01402 return 0; 01403 *ssrc=NULL; 01404 const char* pBuffer=NULL; 01405 int retVal=5; 01406 if(strstr(buffer,"multicast")) 01407 *unicast=false; 01408 else 01409 *unicast=true; 01410 01411 pBuffer=strstr(buffer,"Session: "); 01412 if(pBuffer) 01413 sscanf(pBuffer,"%*s %i",sessionID); 01414 else { 01415 dprintf_err("RTSP.parseResponseSetup: no sessionID found???\n"); 01416 return 0; 01417 } 01418 pBuffer=strstr(buffer,"client_port="); 01419 if(pBuffer) { 01420 pBuffer+=strlen("client_port="); 01421 if(1!=sscanf(pBuffer,"%i",clientPort)) 01422 return 0; 01423 } 01424 pBuffer=strstr(buffer,"server_port="); 01425 if(pBuffer) { 01426 pBuffer+=strlen("server_port="); 01427 if(1!=sscanf(pBuffer,"%i",serverPort)) 01428 return 0; 01429 } 01430 // scan for ssrc 01431 pBuffer=strstr(buffer,"ssrc="); 01432 if(pBuffer) { 01433 pBuffer+=strlen("ssrc="); 01434 *ssrc=new char[9]; 01435 strncpy(*ssrc,pBuffer,8); 01436 (*ssrc)[8]='\0'; 01437 } 01438 else 01439 retVal--; 01440 01441 // check for rtx, rtx is not a must-param, so exclude it from retval 01442 if(info) { 01443 u32 rtxSPort=0; 01444 u32 rtxCPort=0; 01445 pBuffer=strstr(buffer,"server_rtx_port="); 01446 if(pBuffer) { 01447 sscanf( (pBuffer+strlen("server_rtx_port=")), 01448 "%u",&rtxSPort); 01449 } 01450 pBuffer=strstr(buffer,"client_rtx_port="); 01451 if(pBuffer) { 01452 sscanf( (pBuffer+strlen("client_rtx_port=")), 01453 "%u",&rtxCPort); 01454 } 01455 info->localPort=rtxCPort; 01456 info->remotePort=rtxSPort; 01457 } 01458 return retVal; 01459 01460 }; 01461 01462 01463 /* parses @param buffer for a sessionid returns INVALID_SESSIONID 01464 * if none is found 01465 */ 01466 u32 RTSP::extractSessionId(const char* buffer) 01467 { 01468 u32 sesKey=INVALID_SESSIONID; 01469 if(buffer==NULL) 01470 return sesKey; 01471 01472 const char* lptr=strstr(buffer,"Session:"); 01473 if(lptr) { 01474 dprintf_full("RTSP::extractSessionId: Detected Session key in SETUP\r\n"); 01475 lptr+=strlen("Session:"); 01476 if(1!=sscanf(lptr,"%u",&sesKey)) { 01477 dprintf_small("RTSP::extractSessionId: malformed RTSP request\n%s\r\n",buffer); 01478 } 01479 } 01480 return sesKey; 01481 }; 01482 01483 /* searches for the sessionId in @param buffer 01484 * if none is found @param return is NULL, otherwise a new string 01485 * is created with the new sessionid 01486 */ 01487 char* RTSP::replaceSessionIdWith(u32 newId,const char* buffer) 01488 { 01489 if(newId==INVALID_SESSIONID || buffer==NULL) 01490 return NULL; 01491 int newSize=strlen(buffer)+10; // approx. ;-) 01492 01493 const char* lptr=strstr(buffer,"Session:"); 01494 if(lptr==NULL) 01495 return NULL; 01496 01497 char* result=new char[newSize]; 01498 lptr+=strlen("Session:"); 01499 // copies till (+incl.) Session: 01500 strncpy(result,buffer,lptr-buffer); 01501 // now add the new SessionId 01502 sprintf(result+(lptr-buffer)," %u\r\n",newId); 01503 lptr=strstr(lptr,"\n"); 01504 if(!lptr) { 01505 // should never happen 01506 dprintf_small("RTSP::replaceSessionIdWith: malformed RTSP request\n%s\r\n",buffer); 01507 delete result; 01508 return NULL; 01509 } 01510 lptr++; // point after \n 01511 // just add the rest of the command 01512 strcat(result,lptr); 01513 return result; 01514 }; 01515 01516 /* 01517 SET_PARAMETER rtsp://example.com/fizzle/foo RTSP/1.0 01518 CSeq: 421 01519 Content-length: 20 01520 Session: 12345678 // Fixme: missing in standard??? no session??? 01521 Content-type: text/parameters 01522 01523 barparam: barstuff 01524 */ 01525 void RTSP::generateSetParameter(const char* url, u32 session, 01526 std::map<char*, char*, struct stringCompare>* params) 01527 { 01528 assert(url && session>0 && params && params->size()>0); 01529 char *tmp = new char[MSG_BUFFER_SIZE]; 01530 int offset=0; 01531 buffer[0]='\0'; 01532 tmp[0]='\0'; 01533 01534 std::map<char*, char*, struct stringCompare>::iterator li; 01535 for(li=params->begin();li!=params->end();++li) { 01536 offset+=sprintf(tmp+offset,"%s: %s\r\n",(*li).first, (*li).second); 01537 } 01538 01539 offset+=sprintf(tmp+offset,"\r\n"); 01540 offset+=2; 01541 sprintf(buffer+strlen(buffer), "SET_PARAMETER %s RTSP/1.0\r\n" 01542 "CSeq: %i\r\n" 01543 "Session: %u\r\n" 01544 "Content-length: %i\r\n" 01545 "Content-type: text/parameters\r\n\r\n", 01546 url,seqNr,session,offset); 01547 strcat(buffer,tmp); 01548 delete [] tmp; 01549 }; 01550 01551 /* GET_PARAMETER rtsp://example.com/fizzle/foo RTSP/1.0 01552 CSeq: 431 01553 Content-Type: text/parameters 01554 Session: 12345678 01555 Content-Length: 15 01556 01557 packets_received 01558 jitter 01559 */ 01560 void RTSP::generateGetParameter(const char* url, const u32 session, 01561 const int numParams, const char** params) 01562 { 01563 // zero params are legal 01564 assert(url && numParams>=0 && session>0); 01565 buffer[0]=0; 01566 if(numParams==0) { 01567 sprintf(buffer,"GET_PARAMETER %s RTSP/1.0\r\nCSeq: %i\r\nSession: %u\r\n\r\n", 01568 url,seqNr,session); 01569 return; 01570 } 01571 01572 // calc content-length 01573 u32 length=0; 01574 int i=0; 01575 for(i=0;i<numParams;i++) 01576 length+=(strlen(params[i])+2); //add \r\n 01577 if(length>0) 01578 length+=strlen("\r\n\r\n"); 01579 01580 int sz=sprintf(buffer,"GET_PARAMETER %s RTSP/1.0\r\n" 01581 "CSeq: %i\r\n" 01582 "Content-Type: text/parameters\r\n" 01583 "Session: %u\r\n", 01584 url,seqNr,session); 01585 if(length>0 && sz>0) { 01586 sz+=sprintf(buffer+sz,"Content-Length: %u\r\n\r\n",length); 01587 for(i=0;i<numParams;i++) 01588 sz+=sprintf(buffer+sz,"%s\r\n",params[i]); 01589 } 01590 strcat(buffer,"\r\n"); 01591 01592 }; 01593 01594 void RTSP::parseGetParameter(const char* request, u32* session,int* numParams, char*** params) 01595 { 01596 assert(session && numParams && params); 01597 *session=0; 01598 *numParams=0; 01599 *params=NULL; 01600 const char* lptr=NULL; 01601 u32 length=0; 01602 lptr=strstr(request,"Session:"); 01603 if(lptr) { 01604 lptr+=strlen("Session:"); 01605 while(*lptr==' ') 01606 lptr++; 01607 sscanf(lptr,"%u",session); 01608 } 01609 lptr=strstr(request,"Content-Length:"); 01610 if(lptr) { 01611 lptr+=strlen("Content-Length:"); 01612 while(*lptr==' ') 01613 lptr++; 01614 sscanf(lptr,"%u",&length); 01615 } 01616 if(lptr && length>0) { 01617 // count \r\n to determine numParams 01618 const char* tptr=lptr; 01619 int p=0; 01620 while((tptr=strstr(tptr,"\r\n"))!=NULL) { 01621 p++; 01622 tptr+=2; 01623 } 01624 p-=3; // one \r\n from prev line, one empty line before + one after 01625 *numParams=p; 01626 *params=new char*[p]; 01627 // now extract single params 01628 tptr=lptr; 01629 p=0; 01630 while((tptr=strstr(tptr,"\r\n"))!=NULL) { 01631 if((tptr-lptr)>2) { 01632 // not an empty line 01633 (*params)[p]=new char[tptr-lptr+1]; 01634 strncpy((*params)[p],lptr,tptr-lptr); 01635 (*params)[p][tptr-lptr]='\0'; 01636 dprintf_full("RTSP::parseGetParameter found param %s\n",(*params)[p]); 01637 p++; 01638 } 01639 lptr=(tptr+=2); // next line 01640 } 01641 } 01642 }; 01643 /* 01644 SET_PARAMETER rtsp://example.com/fizzle/foo RTSP/1.0 01645 CSeq: 421 01646 Content-length: 20 01647 Session: 12345678 // Fixme: missing in standard??? no session??? 01648 Content-type: text/parameters 01649 01650 barparam: barstuff 01651 */ 01652 void RTSP::parseSetParameter(const char* remaining, u32* session,std::map<char*, char*, struct stringCompare>* params) 01653 { 01654 assert(remaining && session && params); 01655 *session=0; 01656 const char* lptr=strstr(remaining,"Session:"); 01657 if(lptr) { 01658 lptr+=strlen("Session:"); 01659 while(*lptr==' ') 01660 ++lptr; 01661 sscanf(lptr,"%u",session); 01662 } 01663 // no session could be valid, so continue always 01664 lptr=strstr(remaining,"Content-type:"); 01665 if( !(lptr && (lptr=strstr(lptr,"text/parameters")))) { 01666 dprintf_full("RTSP::parseSetParameter illegal content: X%sX\n",remaining); 01667 return; 01668 } 01669 // lptr points to text/.. 01670 lptr=strstr(lptr,"\r\n"); 01671 if(lptr) 01672 lptr+=2; 01673 else 01674 return; 01675 01676 remaining=lptr; 01677 while((lptr=strstr(lptr,"\r\n")) != NULL) { 01678 if(lptr-remaining>2) { 01679 // we have a line starting with remaining 01680 // ending at pos lptr 01681 // we have two strings in this line "a: b" 01682 const char* sepToken=strstr(remaining,":"); 01683 if(!sepToken) 01684 return; 01685 char* param1=new char[sepToken-remaining+1]; 01686 strncpy(param1,remaining,sepToken-remaining); 01687 param1[sepToken-remaining]='\0'; 01688 sepToken++; //skip after : 01689 while(*sepToken == ' ') 01690 ++sepToken; 01691 char* param2=new char[lptr-sepToken+1]; 01692 strncpy(param2,sepToken,lptr-sepToken); 01693 param2[lptr-sepToken]='\0'; 01694 (*params)[param1]=param2; 01695 } 01696 remaining=(lptr+=2); 01697 } 01698 }; 01699 /* 01700 RTSP/1.0 200 OK 01701 CSeq: 431 01702 Content-Length: 46 01703 Content-Type: text/parameters 01704 01705 packets_received: 10 01706 jitter: 0.3838 01707 */ 01708 void RTSP::generateResponseGetParameter(std::map<char*, char*, struct stringCompare>* params) 01709 { 01710 assert(params); 01711 if(!params || (params && params->size()==0)) { 01712 this->generateOk(); 01713 } 01714 else { 01715 // calculate content length 01716 char *tmp = new char[MSG_BUFFER_SIZE]; 01717 int offset=0; 01718 std::map<char*, char*, struct stringCompare>::iterator li; 01719 for(li=params->begin();li!=params->end();++li) { 01720 offset+=sprintf(tmp+offset,"%s: %s\r\n",(*li).first, (*li).second); 01721 } 01722 strcat(tmp+offset,"\r\n"); 01723 offset+=4; 01724 sprintf(buffer,"RTSP/1.0 200 OK\r\nCSeq: %i\r\n" 01725 "Content-Length: %i\r\n" 01726 "Content-Type: text/parameters\r\n\r\n", 01727 seqNr,offset); 01728 strcat(buffer,tmp); 01729 delete [] tmp; 01730 } 01731 }; 01732 01733 /* 01734 RTSP/1.0 451 Invalid Parameter 01735 CSeq: 421 01736 Content-length: 10 01737 Content-type: text/parameters 01738 01739 barparam 01740 */ 01741 void RTSP::generateInvalidParameter(const int numParams, const char* const * const param) 01742 { 01743 buffer[0]='\0'; 01744 if(numParams<=0) { 01745 sprintf(buffer,"RTSP/1.0 451 Invalid Parameter\r\nCSeq: %i\r\n\r\n",seqNr); 01746 return; 01747 } 01748 int length=4; 01749 int i=0; 01750 for(i=0;i<numParams;i++) 01751 length+=(strlen(param[i])+2); 01752 01753 length=sprintf(buffer,"RTSP/1.0 451 Invalid Parameter\r\n" 01754 "CSeq: %i\r\n" 01755 "Content-length: %i\r\n" 01756 "Content-type: text/parameters\r\n\r\n",seqNr,length); 01757 01758 for(i=0;i<numParams;i++) 01759 length+=sprintf(buffer+length,"%s\r\n",param[i]); 01760 01761 strcpy(buffer+length,"\r\n"); 01762 }; 01763 01764 void RTSP::generateNotEnoughBandwidth() 01765 { 01766 sprintf(buffer,"RTSP/1.0 453 Not Enough Bandwidth\r\nCSeq: %i\r\n\r\n",seqNr); 01767 }; 01768 01769 #ifndef WINCE 01770 01776 /* REDIRECT rtsp://example.com/fizzle/foo RTSP/1.0 01777 CSeq: 732 01778 Location: rtsp://bigserver.com:8001 01779 Range: clock=19960213T143205Z- 01780 */ 01781 void RTSP::generateRedirect(const char* url, const char* newUrl, 01782 const time_t* redirectAllowedAtTimePoint) 01783 { 01784 int offset=sprintf(buffer,"REDIRECT %s RTSP/1.0\r\n" 01785 "CSeq: %i\r\n" 01786 "Location: %s\r\n",url,seqNr,newUrl); 01787 if(redirectAllowedAtTimePoint) { 01788 struct tm* tl=localtime(redirectAllowedAtTimePoint); 01789 char timeStr[30]; 01790 strftime(timeStr, 30, "%Y%m%dT%H%M%SZ-", tl); 01791 sprintf(offset+buffer,"Range: clock=%s\r\n\r\n",timeStr); 01792 } 01793 else 01794 sprintf(buffer+offset,"\r\n"); 01795 } 01796 01800 void RTSP::parseCMDRedirect(Session* ses, const Url *fileName, const char* remaining, 01801 char** newUrl, time_t* redirectAllowedAtTimePoint) 01802 { 01803 assert(remaining); 01804 assert(*newUrl==NULL); // otherwise mem leak created 01805 assert(redirectAllowedAtTimePoint); 01806 01807 time(redirectAllowedAtTimePoint); 01808 *newUrl=NULL; 01809 01810 const char* pStr=strstr(remaining,"Location:"); 01811 if(!pStr) 01812 return; 01813 01814 while(pStr[0]==' ') 01815 pStr++; 01816 // points to first char of url 01817 const char* pStrEnd=strstr(pStr,"\r\n"); 01818 if(!pStrEnd) // invalid request 01819 return; 01820 pStrEnd--; 01821 if(pStr>=pStrEnd) 01822 return; 01823 while(pStrEnd[0]==' ') 01824 pStrEnd--; 01825 //pStrEnd points to the last Char of the Url 01826 *newUrl=new char[pStrEnd-pStr+2]; 01827 strncpy(*newUrl,pStr,pStrEnd-pStr+1); 01828 (*newUrl)[pStrEnd-pStr+1]='\0'; 01829 01830 // now search for timepoint 01831 pStr=strstr(remaining,"Range:"); 01832 if(!pStr) 01833 return; 01834 pStr=strstr(pStr,"clock="); 01835 if(!pStr) 01836 return; 01837 pStr+=strlen("clock="); 01838 01839 //we now point to the first char of the timestring 01840 struct tm val; 01841 if(6==sscanf(pStr,"%4i%2i%2iT%2i%2i%2i", 01842 &val.tm_year,&val.tm_mon,&val.tm_mday,&val.tm_hour,&val.tm_min,&val.tm_sec)) { 01843 time_t t=mktime(&val); 01844 *redirectAllowedAtTimePoint=t; 01845 } 01846 01847 }; 01848 #endif 01849 01850 void RTSP::freeMapMemory(std::map<char*,char*,struct stringCompare>* params) 01851 { 01852 std::map<char*,char*,struct stringCompare>::iterator p2It; 01853 for(p2It=params->begin();p2It!=params->end();++p2It) { 01854 if( (*p2It).first) 01855 delete (*p2It).first; 01856 if( (*p2It).second) 01857 delete (*p2It).second; 01858 } 01859 } 01860 01861 void RTSP::doTest() 01862 { 01863 RTSP r; 01864 std::map<char*,char*,struct stringCompare> params; 01865 std::map<char*,char*,struct stringCompare> params2; 01866 params["MINBANDWIDTH"]="128000"; 01867 params["BESTBANDWIDTH"]="512000"; 01868 params["MAXBANDWIDTH"]=""; 01869 r.generateSetParameter("rtsp://server/dummy.mp4",12345,&params); 01870 const char* tmp=r.getBuffer(); 01871 fprintf(stderr,"%s",tmp); 01872 u32 session; 01873 r.parseSetParameter(tmp,&session,&params2); 01874 01875 assert(strcmp(params["MINBANDWIDTH"],params2["MINBANDWIDTH"])==0); 01876 assert(strcmp(params["BESTBANDWIDTH"],params2["BESTBANDWIDTH"])==0); 01877 assert(strlen(params2["MAXBANDWIDTH"])==0); 01878 01879 //free params2 mem 01880 std::map<char*,char*,struct stringCompare>::iterator p2It; 01881 RTSP::freeMapMemory(&params2); 01882 }; 01883