Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | File List | Namespace Members | Class Members | File Members
SimpleStatistics.cpp
00001 #ifndef WINCE 00002 00003 #include "SimpleStatistics.hpp" 00004 #include <assert.h> 00005 #include "ESInfo.hpp" 00006 #include "sord/directory.h" 00007 #include "sord/entry.h" 00008 00011 SimpleStatistics::SimpleStatistics(const char* saveToFileName,const ESInfo* esi, bool s, 00012 bool overwriteFileIfExists): serverMode(s) 00013 { 00014 assert(saveToFileName); 00015 assert(VITOOKI_SIMPLESTATISTICS_MAXSECONDS%2==0); 00016 00017 memset(fileName,0,MAX_STR_LEN); 00018 strncpy(fileName,saveToFileName,MAX_STR_LEN-1); 00019 es=esi; 00020 if(overwriteFileIfExists) { 00021 FILE* fp=fopen(fileName,"wb"); // deletes the file 00022 if(fp) 00023 fclose(fp); 00024 } 00025 currentPos=-1;lastNotSavedPos=0; 00026 lastSecSaved=0; 00027 playCalled=false;teardownCalled=false; 00028 startUpDelayInMS=lastCTS=lastGetCTS=0; 00029 highestCTSSeen=0; 00030 }; 00031 00032 SimpleStatistics::~SimpleStatistics() { 00033 if(!teardownCalled) 00034 teardown(); 00035 }; 00036 00037 void SimpleStatistics::insertFrame(u32 cts, u32 bytes,u32 seqNrStart,u32 seqNrEnd) 00038 { 00039 assert(seqNrStart<=seqNrEnd); 00040 if(currentPos==-1) { 00041 // initial insert, measure startup delay 00042 struct timeval t; 00043 gettimeofday(&t,NULL); 00044 s32 tmp=SimpleStatistics::calcDiffInMS(startTime,t); 00045 if(tmp<0) { 00046 dprintf_err("SimpleStatistics::insertFrame: Negative startupDelay!\n"); 00047 tmp=0; 00048 } 00049 startUpDelayInMS=(u32)tmp; 00050 } 00051 // detect when we have a new second 00052 detectNewSecond(); 00053 // now add the frame 00054 // in clientmode the "In" values are increased 00055 if(!serverMode) { 00056 stats[currentPos].numBytesIn+=bytes; 00057 stats[currentPos].numFramesIn++; 00058 stats[currentPos].numPacketsIn+=(seqNrEnd-seqNrStart+1); 00059 } 00060 else { // in server mode the "out" values 00061 stats[currentPos].numBytesOut+=bytes; 00062 stats[currentPos].numFramesOut++; 00063 stats[currentPos].numPacketsOut+=(seqNrEnd-seqNrStart+1); 00064 } 00065 // check TS 00066 if(stats[currentPos].firstTS>seqNrStart) 00067 stats[currentPos].firstTS=seqNrStart; 00068 if(stats[currentPos].lastTS<seqNrEnd) 00069 stats[currentPos].lastTS=seqNrEnd; 00070 // check CTS 00071 if(stats[currentPos].firstCTS>cts) 00072 stats[currentPos].firstCTS=cts; 00073 if(stats[currentPos].lastCTS<cts) 00074 stats[currentPos].lastCTS=cts; 00075 00076 // adding a frame increases buffer in clientMode 00077 if(!serverMode) { 00078 stats[currentPos].bytesBuffered+=bytes; 00079 stats[currentPos].packetsBuffered+=(seqNrEnd-seqNrStart); 00080 // calc ms depending on the cts range 00081 // catch out of order inserts 00082 if(cts>highestCTSSeen) 00083 highestCTSSeen=cts; 00084 u32 msDiff=(u32)(1000*(highestCTSSeen-lastGetCTS))/(es->getMediaTimeScale()); 00085 if(msDiff>stats[currentPos].msBuffered) 00086 stats[currentPos].msBuffered=msDiff; 00087 dprintf_full("SimpleStatistics::insertFrame lastCTSIn %u lastCTSOut %u msDiff %u\n",cts,lastGetCTS,msDiff); 00088 } 00089 } 00090 void SimpleStatistics::insertPacket(u32 cts, u32 bytes, u32 seqNr, bool firstPacketOfFrame) 00091 { 00092 if(currentPos==-1) { 00093 // initial insert, measure startup delay 00094 struct timeval t; 00095 gettimeofday(&t,NULL); 00096 s32 tmp=SimpleStatistics::calcDiffInMS(startTime,t); 00097 if(tmp<0) { 00098 dprintf_err("SimpleStatistics::insertFrame: Negative startupDelay!\n"); 00099 tmp=0; 00100 } 00101 startUpDelayInMS=(u32)tmp; 00102 stats[0].firstCTS=0; 00103 } 00104 // detect when we have a new second 00105 detectNewSecond(); 00106 if(cts>highestCTSSeen) 00107 highestCTSSeen=cts; 00108 // now add the packet 00109 // in clientmode the "In" values are increased 00110 if(!serverMode) { 00111 stats[currentPos].numBytesIn+=bytes; 00112 if(firstPacketOfFrame) 00113 stats[currentPos].numFramesIn++; 00114 stats[currentPos].numPacketsIn++; 00115 } 00116 else { // in server mode the "out" values 00117 stats[currentPos].numBytesOut+=bytes; 00118 if(firstPacketOfFrame) 00119 stats[currentPos].numFramesOut++; 00120 stats[currentPos].numPacketsOut++; 00121 } 00122 // check TS 00123 if(stats[currentPos].firstTS>seqNr) 00124 stats[currentPos].firstTS=seqNr; 00125 if(stats[currentPos].lastTS<seqNr) 00126 stats[currentPos].lastTS=seqNr; 00127 // check CTS, do not include headers! 00128 if(cts!=0) { 00129 if(stats[currentPos].firstCTS>cts) 00130 stats[currentPos].firstCTS=cts; 00131 if(stats[currentPos].lastCTS<cts) 00132 stats[currentPos].lastCTS=cts; 00133 } 00134 // adding a packet increases buffer in clientMode 00135 if(!serverMode) { 00136 stats[currentPos].bytesBuffered+=bytes; 00137 stats[currentPos].packetsBuffered++; 00138 // calc ms depending on the cts range 00139 // catch out of order inserts 00140 u32 msDiff=(u32)(1000*(highestCTSSeen-lastGetCTS))/(es->getMediaTimeScale()); 00141 // catch packets sent out-of-order 00142 if(msDiff>stats[currentPos].msBuffered) 00143 stats[currentPos].msBuffered=msDiff; 00144 dprintf_full("SimpleStatistics::insertPacket lastCTSIn %u lastCTSOut %u msDiff %u buf %u\n", 00145 cts,lastGetCTS,msDiff,stats[currentPos].msBuffered); 00146 } 00147 }; 00148 00149 void SimpleStatistics::detectNewSecond() 00150 { 00151 if(currentPos==-1 || (currentPos>=0 && stats[currentPos].second!=SimpleStatistics::getCurrentSecond())) { 00152 u32 curBufferMS=0;u32 curBufferPak=0;u32 curBytesBuf=0; 00153 if(currentPos>=0) { 00154 // copy buffer from last, but only in clientMode! 00155 if(!serverMode) { 00156 curBufferMS=stats[currentPos].msBuffered; 00157 curBufferPak=stats[currentPos].packetsBuffered; 00158 curBytesBuf=stats[currentPos].bytesBuffered; 00159 } 00160 } 00161 else { // currentPos is -1 00162 lastNotSavedPos=0; 00163 stats[0].firstCTS=0; 00164 } 00165 currentPos++; // switch to next free position 00166 if(currentPos==VITOOKI_SIMPLESTATISTICS_MAXSECONDS/2) { 00167 // if we enter the second half, save the second half! 00168 if(lastNotSavedPos==VITOOKI_SIMPLESTATISTICS_MAXSECONDS/2) 00169 saveToFile(lastNotSavedPos,VITOOKI_SIMPLESTATISTICS_MAXSECONDS-1); 00170 } 00171 else if(currentPos>=VITOOKI_SIMPLESTATISTICS_MAXSECONDS) { // handle overflow 00172 saveToFile(0,VITOOKI_SIMPLESTATISTICS_MAXSECONDS/2-1); // save the lower half to disk 00173 currentPos=0; // start from the beginning 00174 } 00175 stats[currentPos].reset(SimpleStatistics::getCurrentSecond()); // reset the entry 00176 // correct buffer entries 00177 stats[currentPos].msBuffered=curBufferMS; 00178 stats[currentPos].packetsBuffered=curBufferPak; 00179 stats[currentPos].bytesBuffered=curBytesBuf; 00180 } 00181 } 00182 00183 void SimpleStatistics::getFrame(u32 cts,u32 bytes,u32 seqNrStart,u32 seqNrEnd) 00184 { 00185 // serverMode doesn't support getFrame 00186 if(!serverMode) { 00187 // detect when we have a new second 00188 detectNewSecond(); 00189 00190 // the out values are used as bytes-consumed entries 00191 stats[currentPos].numBytesOut+=bytes; 00192 stats[currentPos].numFramesOut++; 00193 stats[currentPos].numPacketsOut+=(seqNrEnd-seqNrStart+1); 00194 if(lastGetCTS<cts) 00195 lastGetCTS=cts; 00196 00197 00198 // if we get a frame we reduce buffer 00199 u32 msDiff=(u32)(1000*(highestCTSSeen-lastGetCTS)/(es->getMediaTimeScale())); 00200 stats[currentPos].msBuffered=msDiff; 00201 dprintf_full("SimpleStatistics::getFrame lastCTSIn %u lastCTSOut %u msDiff %u\n",highestCTSSeen,lastGetCTS,msDiff); 00202 stats[currentPos].bytesBuffered-=bytes; 00203 stats[currentPos].packetsBuffered-=(seqNrEnd-seqNrStart+1); 00204 } 00205 00206 } 00208 void SimpleStatistics::dropInsertedPacket(u32 bytes, u32 seqNr, u32 cts,bool noOtherPacketsWereDroppedForThisFrame) 00209 { 00210 00211 // only available in clientMode 00212 if(!serverMode) { 00213 detectNewSecond(); 00214 // now search: to which second does the dropped packet belong 00215 int index=currentPos; 00216 bool stop=false; 00217 while(index>=lastNotSavedPos && !stop) { 00218 if(stats[index].firstTS<=seqNr && seqNr<=stats[index].lastTS) { 00219 stop=true; 00220 } 00221 else 00222 index--; 00223 } 00224 // note if we didn't find a position, we will assume the packet 00225 // arrived at pos lastNotSavedPos! 00226 if(index<lastNotSavedPos) 00227 index=lastNotSavedPos; 00228 stats[index].packetsDropped++; 00229 // now correct buffer values for all entries 00230 00231 // a dropped packet only reduces buffer 00232 if(lastGetCTS<cts) 00233 lastGetCTS=cts; 00234 for(int i=index;i<=currentPos;i++) { 00235 stats[i].bytesBuffered-=bytes; 00236 stats[i].packetsBuffered--; 00237 if(noOtherPacketsWereDroppedForThisFrame) { 00238 // reduce buffer time by one frame 00239 u32 decr=((es->getVOPTimeIncrement()*1000)/es->getMediaTimeScale()); 00240 if(stats[i].msBuffered>decr) 00241 stats[i].msBuffered-=decr; 00242 else 00243 stats[i].msBuffered=0; 00244 } 00245 } 00246 // Fix possible rounding errors 00247 u32 msDiff=(u32)(highestCTSSeen-lastGetCTS)/(es->getMediaTimeScale()); 00248 stats[currentPos].msBuffered=msDiff; 00249 dprintf_full("SimpleStatistics::dropInsertedPacket lastCTSIn %u lastCTSOut %u msDiff %u\n",cts,lastGetCTS,msDiff); 00250 } 00251 } 00252 00253 void SimpleStatistics::notifyLostPacket(u32 seqNr) 00254 { 00255 // only available in clientMode 00256 if(!serverMode) { 00257 detectNewSecond(); 00258 // now search: to which second does the dropped packet belong 00259 int index=currentPos; 00260 bool stop=false; 00261 while(index>=lastNotSavedPos && !stop) { 00262 if(stats[index].firstTS<=seqNr && seqNr<=stats[index].lastTS) { 00263 stop=true; 00264 } 00265 else 00266 index--; 00267 } 00268 // note if we didn't find a position, we will assume the packet 00269 // arrived at pos lastNotSavedPos! 00270 if(index<lastNotSavedPos) 00271 index=lastNotSavedPos; 00272 stats[index].packetsLost++; 00273 } 00274 }; 00275 00278 void SimpleStatistics::play(u32 cts,bool clearBuffers) 00279 { 00280 detectNewSecond(); 00281 if(!playCalled) { 00282 gettimeofday(&startTime,NULL); 00283 playCalled=true; 00284 } 00285 else { 00286 if(clearBuffers) { 00287 stats[currentPos].reset(stats[currentPos].second); 00288 } 00289 } 00290 }; 00291 void SimpleStatistics::pause() 00292 { 00293 00294 }; 00295 00296 void SimpleStatistics::teardown() 00297 { 00298 // save not saved entries 00299 saveToFile(lastNotSavedPos,currentPos); 00300 teardownCalled=true; 00301 } 00302 00303 00306 void SimpleStatistics::saveToFile(int startPos,int endPos) 00307 { 00308 dprintf_full("SimpleStatistics::saveToFile start %i end %i\n",startPos,endPos); 00309 if(startPos<0 || endPos<0) 00310 return; 00311 if(endPos<startPos) 00312 endPos+=VITOOKI_SIMPLESTATISTICS_MAXSECONDS; 00313 FILE* fp=fopen(this->fileName,"ab"); 00314 if(!fp) { 00315 dprintf_err("SimpleStatistics::saveToFile: Failed to save statistic\n"); 00316 return; 00317 } 00318 StatEntry tmp(0); 00319 if(lastSecSaved==0) // the very first time when we save 00320 lastSecSaved=stats[startPos%VITOOKI_SIMPLESTATISTICS_MAXSECONDS].second; 00321 00322 fprintf(fp,"# sec\t\tpakIn\tbytIn\tfrmIn\tpakOut\tbytOut\tfrmOut\tpakBuf\tbytBuf\tmsBuf\tpakDro\tbegTS\tendTS\tpakLost\tfirstCTS\tlastCTS\n"); 00323 fprintf(fp,"# startupdelayinMS=%u\n",startUpDelayInMS); 00324 for(int i=startPos;i<=endPos;i++) { 00325 // empty second? no data received? 00326 if(stats[i%VITOOKI_SIMPLESTATISTICS_MAXSECONDS].firstCTS==0xffffffffu) 00327 stats[i%VITOOKI_SIMPLESTATISTICS_MAXSECONDS].firstCTS=0; 00328 if(stats[i%VITOOKI_SIMPLESTATISTICS_MAXSECONDS].firstTS==0xffffffffu) 00329 stats[i%VITOOKI_SIMPLESTATISTICS_MAXSECONDS].firstTS=0; 00330 00331 while(lastSecSaved<stats[i%VITOOKI_SIMPLESTATISTICS_MAXSECONDS].second-1) { 00332 // save 0 entries which are not included in the array 00333 lastSecSaved++; 00334 tmp.reset(lastSecSaved); 00335 tmp.firstCTS=tmp.firstTS=0; // no packets seen 00336 // buffer did not change! 00337 tmp.bytesBuffered=stats[i%VITOOKI_SIMPLESTATISTICS_MAXSECONDS].bytesBuffered; 00338 tmp.packetsBuffered=stats[i%VITOOKI_SIMPLESTATISTICS_MAXSECONDS].packetsBuffered; 00339 tmp.msBuffered=stats[i%VITOOKI_SIMPLESTATISTICS_MAXSECONDS].msBuffered; 00340 saveOneStatEntry(fp,tmp); 00341 } 00342 saveOneStatEntry(fp,stats[i%VITOOKI_SIMPLESTATISTICS_MAXSECONDS]); 00343 lastSecSaved=stats[i%VITOOKI_SIMPLESTATISTICS_MAXSECONDS].second; 00344 stats[i%VITOOKI_SIMPLESTATISTICS_MAXSECONDS].reset(0); 00345 } 00346 fclose(fp); 00347 lastNotSavedPos=(endPos+1)%VITOOKI_SIMPLESTATISTICS_MAXSECONDS; 00348 00349 }; 00350 00351 u32 SimpleStatistics::getNumBytesOutLastNSeconds(int n) 00352 { 00353 detectNewSecond(); 00354 dprintf_full("SimpleStatistics::getNumBytesOutLastNSeconds(int %i)\n",n); 00355 if(n>30) 00356 n=30; 00357 if(n<=0) 00358 n=1; 00359 // start at currentPos, go back in time 00360 // at most n entries are included in the value 00361 u32 lastIncludedSecond=stats[currentPos].second-n+1; 00362 u32 sumBytesOut=0; 00363 for(int i=currentPos;i>currentPos-n && stats[i].second>=lastIncludedSecond;i--) { 00364 dprintf_full("%i:add %u\n",stats[i].second,stats[i].numBytesOut); 00365 sumBytesOut+=stats[i].numBytesOut; 00366 } 00367 return sumBytesOut; 00368 } 00370 SimpleStatistics::StatEntry SimpleStatistics::getSumOfLastNSeconds(int n) 00371 { 00372 detectNewSecond(); 00373 dprintf_full("SimpleStatistics::getSumOfLastNSeconds(int %i)\n",n); 00374 if(n>30) 00375 n=30; 00376 if(n<=0) 00377 n=1; 00378 // start at currentPos, go back in time 00379 // at most n entries are included in the value 00380 u32 lastIncludedSecond=stats[currentPos].second-n+1; 00381 StatEntry sumBytesOut(0xffffffff); 00382 for(int i=currentPos;i>currentPos-n && stats[i].second>=lastIncludedSecond;i--) { 00383 int j=i; 00384 if(j<0) 00385 j+=VITOOKI_SIMPLESTATISTICS_MAXSECONDS; 00386 sumBytesOut+=stats[i]; 00387 } 00388 return sumBytesOut; 00389 }; 00390 void SimpleStatistics::saveOneStatEntry(FILE* fp, const StatEntry& s) 00391 { 00392 assert(fp); 00393 char buffer[4096]; 00394 if(s.convertToString(buffer,4096)) { 00395 fprintf(fp,"%s\n",buffer); 00396 } 00397 else { 00398 dprintf_err("SimpleStatistics::saveOneStatEntry: text truncated and not saved\n"); 00399 } 00400 } 00401 00402 u32 SimpleStatistics::getCurrentSecond() 00403 { 00404 static struct timeval t; 00405 gettimeofday(&t,NULL); 00406 return (u32)t.tv_sec; 00407 } 00408 u32 SimpleStatistics::getCurrentMilliSecond() 00409 { 00410 static struct timeval t; 00411 gettimeofday(&t,NULL); 00412 return (u32)(t.tv_usec/1000); 00413 } 00414 s32 SimpleStatistics::calcDiffInMS(const struct timeval &oldTime, const struct timeval &newTime) 00415 { 00416 return 1000*(newTime.tv_sec-oldTime.tv_sec)+(newTime.tv_usec-oldTime.tv_usec)/1000; 00417 } 00418 00427 bool SimpleStatistics::createGlobalLog(const char* outFile, const char* srcDir, const char* key1, 00428 const char* key2, const char* key3) 00429 { 00430 assert(outFile && srcDir && key1); 00431 if(!outFile || !srcDir || !key1) 00432 return false; 00433 FILE* outFP=fopen(outFile,"wb"); 00434 if(!outFP) 00435 return false; 00436 00437 // 1. scan directory 00438 // 2. extract min+max time values from directory 00439 // 3. create array of StatEntries 00440 // 4. sum up StatEntries 00441 // 5. save result 00442 sord::directory src(srcDir); 00443 if(!src) { 00444 dprintf_err("SimpleStatistics::createGlobalLog: failed to open %s as dir\n",srcDir); 00445 fclose(outFP); 00446 return false; 00447 } 00448 sord::directory::const_iterator &i = src.begin(); 00449 sord::directory::const_iterator &end = src.end(); 00450 std::list<char*> logFiles; 00451 char cleanDirName[MAX_STR_LEN]; 00452 char tmpBuffer[MAX_STR_LEN]; 00453 strcpy(cleanDirName,srcDir); 00454 StatEntry tmpStat; 00455 if( cleanDirName[strlen(cleanDirName)-1]!='/' && 00456 cleanDirName[strlen(cleanDirName)-1]!='\\') { 00457 strcat(cleanDirName,"/"); 00458 } 00459 u32 firstTime=0xffffffff; 00460 u32 lastTime=0; 00461 u32 invalidFilesFound=0; 00462 for (; i != end; ++i) { 00463 // isa file? 00464 if(i->get_type() == sord::entry::type_regular) { 00465 // does it match the key(s)? 00466 00467 if(strstr(i->get_name(),key1) && 00468 (key2==NULL || (key2!=NULL && strstr(i->get_name(),key2))) && 00469 (key3==NULL || (key3!=NULL && strstr(i->get_name(),key3)))) { 00470 // found an entry, push it back to list 00471 char* tmpFullFileName=new char[strlen(cleanDirName)+strlen(i->get_name())+1]; 00472 sprintf(tmpFullFileName,"%s%s",cleanDirName,i->get_name()); 00473 // now verify the entry 00474 FILE* fp=fopen(tmpFullFileName,"rb"); 00475 if(fp) { 00476 // try reading the first and last entry 00477 u32 tmpStart=0xffffffff; 00478 u32 tmpEnd=0; 00479 while(!feof(fp)) { 00480 if(fgets(tmpBuffer,MAX_STR_LEN,fp)) { 00481 if(StatEntry::createFromString(tmpBuffer,tmpStat)) { 00482 // extract the second 00483 u32 tmpSec=tmpStat.second; 00484 if(tmpSec<tmpStart) 00485 tmpStart=tmpSec; 00486 if(tmpSec>tmpEnd) 00487 tmpEnd=tmpSec; 00488 } 00489 } 00490 } 00491 if(tmpEnd!=0 && tmpStart!=0xffffffff) { 00492 // at least one valid entry is in the file! 00493 // everything okay, now set the new global range 00494 if(lastTime<tmpEnd) 00495 lastTime=tmpEnd; 00496 if(tmpStart<firstTime) 00497 firstTime=tmpStart; 00498 // insert the log file 00499 logFiles.push_back(tmpFullFileName); 00500 } 00501 else { 00502 invalidFilesFound++; 00503 delete[] tmpFullFileName; 00504 } 00505 fclose(fp); 00506 } 00507 else { 00508 invalidFilesFound++; 00509 delete[] tmpFullFileName; 00510 } 00511 } 00512 } 00513 } 00514 if(logFiles.empty()) { 00515 fclose(outFP); 00516 return false; 00517 } 00518 // now create the array of statEntries 00519 int arraySize=lastTime-firstTime+1; 00520 StatEntry* array=new StatEntry[arraySize]; 00521 int j=0; 00522 // reset the entries 00523 for(j=0;j<arraySize;j++) { 00524 array[j].reset(firstTime+j); 00525 } 00526 // now parse all entries in the list and sum up 00527 std::list<char*>::iterator li; 00528 FILE* logFP=NULL; 00529 u32 validFilesFound=0; 00530 while(!logFiles.empty()) { 00531 li=logFiles.begin(); 00532 logFP=fopen( (*li), "rb"); 00533 if(logFP) { 00534 // read all entries 00535 while(!feof(logFP)) { 00536 if(fgets(tmpBuffer,MAX_STR_LEN,logFP)) { 00537 if(StatEntry::createFromString(tmpBuffer,tmpStat)) { 00538 int idx=tmpStat.second-firstTime; 00539 array[idx]+=tmpStat; 00540 } 00541 } 00542 } 00543 fclose(logFP); 00544 validFilesFound++; 00545 } 00546 else { 00547 invalidFilesFound++; 00548 } 00549 delete[] (*li); 00550 logFiles.pop_front(); 00551 } 00552 // dump stat to file 00553 // outFP is already open 00554 // write header 00555 fprintf(outFP,"# Found %u valid files, %u invalid files were ignored\n", 00556 validFilesFound,invalidFilesFound); 00557 for(j=0;j<arraySize;j++) { 00558 SimpleStatistics::saveOneStatEntry(outFP,array[j]); 00559 } 00560 fclose(outFP); 00561 return true; 00562 } 00563 00564 00565 #endif //wince