SDP Class Reference

<class description="" goes="" here=""> RTSP helper class for SDP generation needed as response to an RTSP DESCRIBE More...

#include <SDP.hpp>

List of all members.


Public Member Functions

 SDP ()
 Default constructor.
 ~SDP ()
 Destructor.
bool parse (const char *sdp)
 Parse an SDP description.
const struct session * firstSession () const
 Return the fields of the first session of this SDP description instance.

Static Public Member Functions

char * generate (ContainerInfo *stream, bool perfectHit, const char *filename, const char *localAddr, int SL_PAYLOAD_TYPE, int sizeLength, int dtsDeltaLength, const TerminalCapabilities *tc=NULL, bool includeRtxInfo=false, std::list< rtx_group * > *groups=NULL)
 generates a (S)ession (D)escription (P)rotocol string for the given ContainerInfo object.
void encodeDecoderConfig (u8 *dest, const u8 *source, u32 n)
 encodes the source-decoder config into dest, which must be of size 2n+1
void decodeDecoderConfig (u8 *dest, const u8 *source, u32 srcSize)
 decodes the encoded-decoderConfig stored in source into dest which must be of size srcSize/2

Detailed Description

<class description="" goes="" here=""> RTSP helper class for SDP generation needed as response to an RTSP DESCRIBE

Author:
Peter Schojer
Version:
Id
SDP.hpp,v 1.5 2006/01/20 15:37:54 mkropfbe Exp

Definition at line 67 of file SDP.hpp.


Member Function Documentation

const struct SDP::session * SDP::firstSession  )  const
 

Return the fields of the first session of this SDP description instance.

Returns:
a pointer to the struct of the first session, or NULL if there are no sessions.
See also:
parse(const char *)
Definition at line 166 of file SDP.cpp.
00167 { 00168 return sessionList.empty() ? NULL : sessionList.front(); 00169 }

char * SDP::generate ContainerInfo stream,
bool  perfectHit,
const char *  filename,
const char *  localAddr,
int  SL_PAYLOAD_TYPE,
int  sizeLength,
int  dtsDeltaLength,
const TerminalCapabilities tc = NULL,
bool  includeRtxInfo = false,
std::list< rtx_group * > *  groups = NULL
[static]
 

generates a (S)ession (D)escription (P)rotocol string for the given ContainerInfo object.

Will succeed on objects without any elementary streams! Definition at line 171 of file SDP.cpp.

References encodeDecoderConfig(), TerminalCapabilities::getDisplayHeight(), TerminalCapabilities::getDisplayWidth(), ContainerInfo::getESList(), ContainerInfo::getIODHandle(), TerminalCapabilities::getMaxDecoderBitRateInBit(), TerminalCapabilities::getNetworkCapacityInByte(), TerminalCapabilities::getNumAudioChannels(), and ContainerInfo::getRtxGroup().

00176 { 00177 00178 // mslc.SizeLength + mslc.DTSDeltaLength replaced by 00179 // sizeLength + dtsDeltaLength 00180 assert(stream!=NULL); 00181 char *buffer = new char[MSG_BUFFER_SIZE]; 00182 buffer[0] = 0; 00183 char *next = buffer; 00184 int cnt = 0; 00185 //disable rtx when rtxconfig is missing 00186 if(groups==NULL) 00187 includeRtxInfo = false; 00188 00189 #if defined WIN32 && !(defined WINCE) 00190 struct timeb tv; 00191 ftime(&tv); 00192 cnt = 00193 sprintf(next, "v=0\r\no=%s %i %i IN IP4 %s\r\ns=%s\r\nu=http:///\r\ne=admin@\r\n",SERVER_ID, 00194 (int) tv.time, (int) tv.millitm, localAddr, filename); 00195 #else 00196 timeval tv; 00197 gettimeofday(&tv, 0); 00198 cnt = 00199 sprintf(next, "v=0\r\no=%s %i %i IN IP4 %s\r\ns=%s\r\nu=http:///\r\ne=admin@\r\n",SERVER_ID, 00200 (int) tv.tv_sec, (int) tv.tv_usec, localAddr, filename); 00201 #endif 00202 00203 next += cnt; 00204 list <ESInfo*> * es = stream->getESList(); 00205 list < ESInfo * >::const_iterator li = es->begin(); 00206 float duration = 0; 00207 u32 bitRateInKilobits = 0; 00208 00209 while (li != es->end()) { 00210 if (duration < ((float) (*li)->getDurationInMs() / 1000.0)) { 00211 duration = ((float) (*li)->getDurationInMs() / 1000.0); 00212 } 00213 if ((*li)->isAudioStream() || (*li)->isVisualStream()) { 00214 00215 bitRateInKilobits += ((*li)->getAvgBandwidth() / 1024); 00216 } 00217 ++li; 00218 } 00219 if(tc) { 00220 u32 bw = tc->getNetworkCapacityInByte()*8; 00221 if(bw==0) 00222 bw = tc->getMaxDecoderBitRateInBit(); 00223 else if(tc->getMaxDecoderBitRateInBit() && 00224 tc->getMaxDecoderBitRateInBit() < bw) { 00225 bw = tc->getMaxDecoderBitRateInBit(); 00226 } 00227 if((bw/1024)<bitRateInKilobits) 00228 bitRateInKilobits=bw; 00229 } 00230 /* 00231 Connection Data 00232 00233 c=<network type> <address type> <connection address> 00234 00235 The "c=" field contains connection data. 00236 00237 A session announcement must contain one "c=" field in each media 00238 description (see below) or a "c=" field at the session-level. 00239 */ 00240 cnt = sprintf(next, "c=IN IP4 %s\r\nb=AS:%i\r\nt=0 0\r\n", localAddr, 00241 bitRateInKilobits); 00242 // cnt = sprintf(next, "c=IN IP4 0.0.0.0\r\nb=AS:%i\r\nt=0 0\r\n",bitRateInKilobits); 00243 next += cnt; 00244 00245 char *iodstr = new char[MAX_IOD_SIZE]; 00246 iodstr[0] = 0; 00247 u32 iodHandleSize=0; 00248 00249 #ifndef WINCE 00250 int iodenclen = base64encode(stream->getIODHandle(iodHandleSize), iodHandleSize, 00251 (unsigned char*)iodstr, MAX_IOD_SIZE); 00252 #else 00253 int iodenclen = 0; 00254 #endif 00255 00256 if(iodenclen<0 || iodenclen>=MAX_IOD_SIZE) 00257 iodenclen=0; 00258 iodstr[iodenclen] = 0; 00259 int streamcnt = es->size(); // #streams is without iod 00260 00261 li = es->begin(); //a=control:*\r\n 00262 cnt = sprintf(next, "a=control:*\r\na=mpeg4-iod:\"data:application/mpeg4-iod;base64,%s\"\r\n", 00263 iodstr); 00264 next += cnt; 00265 00266 // 00267 cnt = sprintf(next, "a=isma-compliance:1,1.0,1\r\na=range:npt=0-%5.5f\r\n", 00268 duration); 00269 next += cnt; 00270 00271 if(includeRtxInfo && !es->empty()) { 00272 // write the grouping infos 00273 list < ESInfo*>::const_iterator ei=es->begin(); 00274 u32 lastMid=1; 00275 while(ei!=es->end()) { 00276 if((*ei)->isAudioStream() || 00277 (*ei)->isVisualStream() ) { 00278 rtx_group* group=stream->getRtxGroup( (*ei)->getStreamId(),groups); 00279 if(!group) { 00280 // no entry found, generate one 00281 group=new rtx_group(NACK,3000); 00282 group->midESInfo=lastMid++; 00283 group->rtx->mId=lastMid++; 00284 group->es=(*ei); 00285 group->rtx->ticks=(*ei)->getMediaTimeScale(); 00286 groups->push_back(group); 00287 } 00288 if(!group->rtx) { 00289 group->rtx=new rtx_info(NACK,3000); 00290 group->midESInfo=lastMid++; 00291 group->rtx->mId=lastMid++; 00292 group->es=(*ei); 00293 group->rtx->ticks=(*ei)->getMediaTimeScale(); 00294 } 00295 cnt=sprintf(next,"a=group:FID %u %u\r\n", 00296 group->midESInfo, 00297 group->rtx->mId); 00298 next+=cnt; 00299 } 00300 ++ei; 00301 } 00302 00303 } 00304 int payloadType = SL_PAYLOAD_TYPE; 00305 u32 bw2=0; 00306 00307 for (int i = 1; i <= streamcnt; i++) { 00308 00309 u32 tid = (*li)->getStreamId(); 00310 u32 handlerType = (*li)->getHandlerType(); 00311 //const u8 *decoderConfig = (*li)->getEncodedDecoderConfig(); 00312 // u32 objectType=(*li)->getObjectType(); 00313 // u32 streamType = (*li)->getStreamType(); 00314 // u32 bufferSize=(*li)->getBufferSize(); 00315 u32 sze = 0; 00316 u8 volHeader[4096]; 00317 u8* newHeader=NULL; 00318 u8* src=NULL; 00319 uint width = 0, height = 0;uint bitrate=0; 00320 00321 if ('/' == *filename) 00322 filename++; 00323 rtx_group* currentRtx=NULL; 00324 if(includeRtxInfo) 00325 currentRtx=stream->getRtxGroup(tid,groups); 00326 00327 switch (handlerType) { 00328 case MP4VisualHandlerType: 00329 width = ((VideoESInfo*)(*li))->getWidth(); 00330 height = ((VideoESInfo*)(*li))->getHeight(); 00331 bitrate=((VideoESInfo*)(*li))->getAvgBandwidth(); 00332 if(tc && !perfectHit) { 00333 // check if tc say sth different 00334 if(tc->getDisplayHeight()!=0 && 00335 tc->getDisplayHeight() < height) 00336 height=tc->getDisplayHeight(); 00337 if(tc->getDisplayWidth()!=0 && 00338 tc->getDisplayWidth() < width) 00339 width=tc->getDisplayWidth(); 00340 bw2=tc->getNetworkCapacityInByte()*8; 00341 if(bw2==0) 00342 bw2=tc->getMaxDecoderBitRateInBit(); 00343 else if(tc->getMaxDecoderBitRateInBit() && 00344 tc->getMaxDecoderBitRateInBit() < bw2) { 00345 bw2=tc->getMaxDecoderBitRateInBit(); 00346 } 00347 u32 new_bitrate = width * height * 6; 00348 if (new_bitrate > bw2) 00349 new_bitrate = bw2; //reduce to given termCaps 00350 if (new_bitrate < bitrate) 00351 bitrate = new_bitrate; 00352 newHeader = MP4Encoder::createNewHeader((VideoESInfo*)(*li), new_bitrate, 00353 width, height, 300, (int)((VideoESInfo*)(*li))->getFPS(), &sze); 00354 } 00355 if(!newHeader) { 00356 sze = (*li)->getHeaders(&src); 00357 SDP::encodeDecoderConfig(volHeader, src, sze); 00358 } 00359 else { 00360 SDP::encodeDecoderConfig(volHeader,newHeader,sze); 00361 delete newHeader;newHeader=NULL; 00362 } 00363 cnt = sprintf(next, "m=video 0 RTP/AVP %i\r\nb=AS:%i\r\n" 00364 "a=rtpmap:%i MP4V-ES/%i\r\n" 00365 "a=control:trackID=%i\r\n" 00366 "a=cliprect:0,0,%i,%i\r\n" 00367 "a=fmtp:%i profile-level-id=1;config=%s\r\n" // StreamType=%i; "SizeLength=%i; " 00368 // "DTSDeltaLength=%i; config=%s\r\n" 00369 "a=mpeg4-esid:%i\r\n" 00370 "a=aspect-ratio=%6.6f\r\n" 00371 "a=frame-pattern=%s\r\n" 00372 "a=bframesamount=%f\r\n", 00373 payloadType, (bitrate / 1024), 00374 payloadType, (*li)->getMediaTimeScale(), 00375 tid, 00376 width, height, 00377 SL_PAYLOAD_TYPE, volHeader, // streamType, //sizeLength, 00378 // dtsDeltaLength, volHeader, 00379 tid, // "mpeg4-generic" 00380 ((VideoESInfo*)(*li))->getAspectRatio(), 00381 (((VideoESInfo*)(*li))->hasStaticFramePattern()?"static":"dynamic"), 00382 ((VideoESInfo*)(*li))->getAvgBFrameSize()); 00383 next += cnt; 00384 // add bframes and gop size for static frame pattern 00385 if(((VideoESInfo*)(*li))->hasStaticFramePattern()) { 00386 cnt = sprintf(next, "a=num-bframes %i\r\n" 00387 "a=gop-size %i\r\n", 00388 ((VideoESInfo*)(*li))->getNum_B_frames(), 00389 ((VideoESInfo*)(*li))->getGOP_size()); 00390 next+=cnt; 00391 } 00392 payloadType++; 00393 00394 if( includeRtxInfo && currentRtx && 00395 currentRtx->rtx && currentRtx->rtx->state!=NONE) { 00396 u32 maxRtxDelay=3000; 00397 if( currentRtx->rtx && 00398 currentRtx->rtx->rtxTimeInMs>0) 00399 maxRtxDelay=currentRtx->rtx->rtxTimeInMs; 00400 else { 00401 currentRtx->rtx=new rtx_info(NACK,maxRtxDelay); // impossible code 00402 } 00403 00404 00405 cnt = sprintf(next, 00406 "a=rtcp-fb:%i %s\r\n" 00407 "a=mid:%u\r\n" // write the mid for the media 00408 "m=video 0 RTP/AVPF %i\r\n" 00409 "a=rtpmap:%i rtx/%u\r\n" 00410 "a=fmtp:%i apt=%i;rtx-time=%u\r\n" 00411 "a=mid:%u\r\n", 00412 payloadType-1, sRTXState[currentRtx->rtx->state], 00413 currentRtx->midESInfo, 00414 payloadType, 00415 payloadType, currentRtx->rtx->ticks, 00416 payloadType,(payloadType-1),maxRtxDelay, 00417 currentRtx->rtx->mId); 00418 next += cnt; 00419 currentRtx->rtx->payloadType=payloadType; 00420 payloadType++; 00421 } 00422 break; 00423 case MP4AudioHandlerType: 00424 // only send audio when TermCaps indicate that the client can handle audio 00425 if(!tc || (tc && tc->getNumAudioChannels()>0)) { 00426 // audio config still not correct 00427 u8 decoderConfig[4096]; 00428 src = NULL; 00429 payloadType=14; 00430 sze = (*li)->getHeaders(&src); 00431 SDP::encodeDecoderConfig(decoderConfig, src, sze); 00432 cnt = sprintf(next, "m=audio 0 RTP/AVP %i\r\nb=AS:%i\r\n" // payload 00433 "a=control:trackID=%i\r\n" // trackid 00434 "a=mpeg4-esid:%i\r\n" // esid 00435 "a=fmtp:%i DTSDeltaLength=%i; config=%s\r\n" 00436 "a=rtpmap:%i MPA/%i\r\n", 00437 payloadType,((*li)->getAvgBandwidth() / 1024), tid, tid, 00438 payloadType, dtsDeltaLength, decoderConfig, 00439 payloadType, (*li)->getMediaTimeScale()); 00440 next += cnt; 00441 payloadType++; 00442 00443 if(includeRtxInfo) { 00444 u32 maxRtxDelay=3000; 00445 if( currentRtx->rtx && 00446 currentRtx->rtx->rtxTimeInMs>0) 00447 maxRtxDelay=currentRtx->rtx->rtxTimeInMs; 00448 else { 00449 currentRtx->rtx=new rtx_info(NACK,maxRtxDelay); 00450 } 00451 cnt = sprintf(next, 00452 "a=mid:%ui\r\n" // write the mid for the media 00453 "m=audio 0 RTP/AVPF %i\r\n" 00454 "a=rtpmap:%i rtx/%i\r\n" 00455 "a=fmtp:%i apt=%i;rtx-time=%ui\r\n" 00456 "a=mid:%i\r\n", 00457 currentRtx->midESInfo, 00458 payloadType, 00459 payloadType, currentRtx->rtx->ticks, 00460 payloadType,(payloadType-1),maxRtxDelay, 00461 currentRtx->rtx->mId); 00462 next += cnt; 00463 currentRtx->rtx->payloadType=payloadType; 00464 payloadType++; 00465 } 00466 } // if !tc... 00467 break; 00468 default: //application is not recognized by many players! 00469 /* cnt = sprintf(next, "m=application 0 RTP/AVP %i\r\n" "a=control:trackID=%i\r\n" "a=mpeg4-esid:%i\r\n" "a=fmtp:%i " // StreamType=%i; "// SizeLength=%i; " 00470 // "config=%s\r\n" // "DTSDeltaLength=%i; config=%s\r\n" 00471 "\r\na=rtpmap:%i %s\r\n", payloadType, tid, tid, payloadType, // streamType, //sizeLength, 00472 //dtsDeltaLength, decoderConfig, 00473 // payloadType, "X-MP4-ES/1000"); //mpeg4-generic 00474 next += cnt; 00475 payloadType++; */ 00476 ; 00477 } 00478 ++li; // increase list iterator 00479 if(src) 00480 delete src; 00481 } 00482 00483 *next = 0; 00484 char *result = new char[strlen(buffer) + 1]; 00485 strcpy(result, buffer); 00486 00487 delete [] iodstr; 00488 delete [] buffer; 00489 return result; 00490 };

bool SDP::parse const char *  sdp  ) 
 

Parse an SDP description.

The field values may then be retrieved using other instance methods of this class.

Currently, only the following SDP fields are understood, all other occuring fields are ignored:

  • v=0 required to start a valid SDP description.
  • s=_session_name_ session name; required to start a session description.
  • a=control:_URL_ control URL.
  • a=range:npt=_start_-_end_ media presentation time range in seconds (floating point).

Warning: This function is not thread-safe!

Returns:
false if the given string does not contain a valid SDP description.
Definition at line 83 of file SDP.cpp.
00084 { 00085 char *buf = new char[SDP_BUF_SIZE]; 00086 int bufsize = strlen(sdp)+1; 00087 if (bufsize > SDP_BUF_SIZE) { 00088 delete [] buf; 00089 return false; 00090 } 00091 strcpy(buf, sdp); 00092 struct session *sp = NULL; 00093 bool err = false; 00094 bool valid_version = false; 00095 char *bufend = buf + strlen(buf); // points to terminating '\0' 00096 char *p = buf; 00097 char *field, *value; 00098 do { 00099 /* ignore empty lines */ 00100 while (p < bufend && (*p == '\n' || *p == '\r')) 00101 p++; 00102 if (p >= bufend) 00103 break; 00104 field = p; 00105 if (++p >= bufend || *p != '=') { 00106 err = true; 00107 break; 00108 } 00109 /* value is anything up to the end of line or buffer */ 00110 value = ++p; 00111 while (p < bufend && *p != '\n' && *p != '\r') 00112 p++; 00113 *p++ = '\0'; 00114 switch (*field) { 00115 case 'v': 00116 if (strcmp(value, "0") == 0) 00117 valid_version = true; 00118 else 00119 err = true; 00120 break; 00121 case 's': 00122 sp = new struct session; 00123 sp->name.assign(value); 00124 sessionList.push_back(sp); 00125 break; 00126 case 'a': 00127 if (sp) { 00128 if (strncmp(value, "control:", 8) == 0) { 00129 sp->url.assign(value + 8); 00130 } else if (strncmp(value, "range:", 6) == 0) { 00131 char *p1 = strstr(value + 6, "npt="); 00132 if (p1) { 00133 p1 += 4; 00134 char *p2 = strchr(p1, '-'); 00135 if (p2) { 00136 *p2++ = '\0'; 00137 if (strlen(p2) > 0 && sscanf(p2, "%lf", &sp->nptEnd) != 1) 00138 err = true; 00139 } 00140 if (strlen(p1) > 0 && sscanf(p1, "%lf", &sp->nptStart) != 1) 00141 err = true; 00142 } 00143 } 00144 } 00145 break; 00146 } // switch 00147 } while (p < bufend); 00148 if (err || !valid_version) { 00149 clear(); 00150 delete [] buf; 00151 return false; 00152 } 00153 #if VITOOKI_DEBUG_LEVEL >= VITOOKI_DEBUG_FULL 00154 dprintf_full("SDP::parse() parsed %d session description(s):\n", sessionList.size()); 00155 for (list<struct session *>::iterator it = sessionList.begin(); 00156 it != sessionList.end(); it++) { 00157 dprintf_full(" name = %s, url = %s, nptStart = %lf, nptEnd = %lf\n", 00158 (*it)->name.c_str(), (*it)->url.c_str(), 00159 (*it)->nptStart, (*it)->nptEnd); 00160 } 00161 #endif 00162 delete [] buf; 00163 return true; 00164 }


The documentation for this class was generated from the following files: