Statistics.cpp

00001 /*********************************************************************** 00002 * * 00003 * ViTooKi * 00004 * * 00005 * title: Statistics.cpp * 00006 * * 00007 * * 00008 * * 00009 * ITEC institute of the University of Klagenfurt (Austria) * 00010 * http://www.itec.uni-klu.ac.at * 00011 * * 00012 * * 00013 * For more information visit the ViTooKi homepage: * 00014 * http://ViTooKi.sourceforge.net * 00015 * vitooki-user@lists.sourceforge.net * 00016 * vitooki-devel@lists.sourceforge.net * 00017 * * 00018 * This file is part of ViTooKi, a free video toolkit. * 00019 * ViTooKi is free software; you can redistribute it and/or * 00020 * modify it under the terms of the GNU General Public License * 00021 * as published by the Free Software Foundation; either version 2 * 00022 * of the License, or (at your option) any later version. * 00023 * * 00024 * This program is distributed in the hope that it will be useful, * 00025 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 00026 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 00027 * GNU General Public License for more details. * 00028 * * 00029 * You should have received a copy of the GNU General Public License * 00030 * along with this program; if not, write to the Free Software * 00031 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, * 00032 * MA 02111-1307, USA. * 00033 * * 00034 ***********************************************************************/ 00035 00036 /*********************************************************************** 00037 * * 00038 * REVISION HISTORY: * 00039 * * 00040 * * 00041 * * 00042 ***********************************************************************/ 00043 00044 #include "Statistics.hpp" 00045 #include "ESInfo.hpp" 00046 00047 00048 00049 /*****************************************************************/ 00050 Statistics::Statistics(ESInfo *esi, bool channelIsWriter) { 00051 es = esi; 00052 00053 00054 //GENERIC STATISTICS 00055 first_packet_dtime=0; 00056 last_packet_dtime=0; 00057 first_packet_ts=0; 00058 highest_packet_ts=0; 00059 playout_sec=0.0; 00060 memset(playsec_stat,0,sizeof(playsec_stat)); 00061 00062 00063 //NETWORK RELATED STUFF 00064 streamout_sec=0; 00065 streamout_float_sec=0.0; 00066 avg_interarrival_time=0; 00067 avg_rtt=0; 00068 stream_bw=0; 00069 memset(streamsec_stat,0,sizeof(streamsec_stat)); 00070 00071 //BUFFER RELATED STUFF 00072 client_preQ_max_size=0; 00073 buf_ahead_sec=0.0; 00074 prefetched_secs=0.0; 00075 stillToPrefetchSecs=0.0; 00076 00077 //ADAPTATION RELATED STUFF 00078 adapt_secs=1.0; 00079 adapt_rate=100; 00080 00081 /* counts the number of opened Stat instances 00082 * this is used for statistics output filenames 00083 */ 00084 static int globalStatsCounter; 00085 char *dummy = new char[MAX_STR_LEN]; 00086 char *path = new char[MAX_STR_LEN]; 00087 int tmp=globalStatsCounter++; 00088 #define STAT_FILE "rtp_stat" 00089 00090 #ifdef VITOOKI_DEBUG 00091 00092 sprintf(path,"."); 00093 if (!channelIsWriter) { 00094 #ifndef WIN32 00095 // sprintf(path,"../muviserver/"); 00096 #endif 00097 sprintf(dummy,"%s/%s-playout-client-%i",path,STAT_FILE,tmp); 00098 } else 00099 sprintf(dummy,"%s/%s-playout-server-%i",path,STAT_FILE,tmp); 00100 if (!(playout_stat_fp=fopen(dummy,"w+")) ) { 00101 #ifndef WINCE 00102 perror("Statistics-playout file"); 00103 #endif 00104 dprintf_full("Statistics: playout file was %s\n",dummy); 00105 assert(playout_stat_fp); 00106 } 00107 00108 sprintf(path,"."); 00109 if (!channelIsWriter) { 00110 #ifndef WIN32 00111 // sprintf(path,"../muviserver/"); 00112 #endif 00113 sprintf(dummy,"%s/%s-streamout-client-%i",path,STAT_FILE,tmp); 00114 } else 00115 sprintf(dummy,"%s/%s-streamout-server-%i",path,STAT_FILE,tmp); 00116 00117 if (!(streamout_stat_fp=fopen(dummy,"w+")) ) { 00118 #ifndef WINCE 00119 perror("Statistics-streamout file"); 00120 #endif 00121 dprintf_full("Statistics: streamout file was %s\n",dummy); 00122 assert(streamout_stat_fp); 00123 } 00124 00125 dprintf_full("Statistics::Statistics created stat files number %i for file %s\n",tmp,dummy); 00126 00127 #endif 00128 00129 delete [] dummy; 00130 delete [] path; 00131 } 00132 00133 /*****************************************************************/ 00134 Statistics::~Statistics() { 00135 fclose(streamout_stat_fp); 00136 fclose(playout_stat_fp); 00137 } 00138 00139 /*****************************************************************/ 00140 void Statistics::setESInfo(ESInfo *new_es) { 00141 es = new_es; 00142 streamsec_stat[getStreamoutSec() % RTP_STAT_SECS].stream_switched = true; 00143 playsec_stat[(int)floor((float)highest_packet_ts / (float)es->getMediaTimeScale()) % RTP_STAT_SECS].stream_switched = true; 00144 } 00145 00146 00147 /*****************************************************************/ 00148 int Statistics::setAvgRTT(int curr_rtt) { 00149 if (getAvgRTT() == 0) 00150 avg_rtt = curr_rtt; 00151 else 00152 avg_rtt = (int) ((float)getAvgRTT() 00153 * RTP_AVG_RTT_ALPHA + curr_rtt 00154 * (1-RTP_AVG_RTT_ALPHA) ); 00155 00156 return avg_rtt; 00157 } 00158 00159 /*****************************************************************/ 00160 int Statistics::setAvgInterarrivalTime(int msecs) { 00161 if (getAvgInterarrivalTime() == 0) 00162 avg_interarrival_time = msecs; 00163 else 00164 avg_interarrival_time = (int) ((float)getAvgInterarrivalTime() 00165 * RTP_AVG_RTT_ALPHA + msecs 00166 * (1-RTP_AVG_RTT_ALPHA) ); 00167 return avg_interarrival_time; 00168 } 00169 00170 00171 /*****************************************************************/ 00172 long Statistics::getBWfromStreamoutSec(u32 sec) { 00173 if ((sec > getStreamoutFullSec()) 00174 || ((signed)sec < MAX(0,(signed)getStreamoutFullSec() - RTP_STAT_SECS)) ) { 00175 return -1; 00176 } 00177 00178 //dprintf_full("Statistics::getBWfromStreamoutSec %i data|rtx|nacked %6i:%6i:%6i\n",sec, 00179 //getStreamoutSecDataBW(sec), getStreamoutSecRtxBW(sec), getStreamoutSecNackedBW(sec)); 00180 return getStreamoutSecDataBW(sec) + getStreamoutSecRtxBW(sec) - getStreamoutSecNackedBW(sec); 00181 } 00182 00183 /*****************************************************************/ 00184 long Statistics::getBWfromPlayoutSec(u32 sec) { 00185 if ((sec > (unsigned)ceil(getPlayoutSec())) 00186 || ((signed)sec < MAX(0,(signed)ceil(getPlayoutSec()) - RTP_STAT_SECS)) ) { 00187 return -1; 00188 } 00189 00190 //dprintf_full("Statistics::getBWfromStreamoutSec %i data|rtx|nacked %6i:%6i:%6i\n",sec, 00191 //getStreamoutSecDataBW(sec), getStreamoutSecRtxBW(sec), getStreamoutSecNackedBW(sec)); 00192 return getPlayoutSecDataBW(sec) + getPlayoutSecRtxBW(sec) - getPlayoutSecNackedBW(sec); 00193 } 00194 00195 00196 /*****************************************************************/ 00198 u32 Statistics::getNumStreamoutSecs() { 00199 return RTP_STAT_SECS; 00200 } 00201 00202 00203 /*****************************************************************/ 00204 bool Statistics::incPlayoutSecPSNR(int sec, double psnr) { 00205 playsec_stat[sec % RTP_STAT_SECS].sum_psnr += psnr; 00206 playsec_stat[sec % RTP_STAT_SECS].num_psnr++; 00207 return true; 00208 }; 00209 00210 00211 /*****************************************************************/ 00212 double Statistics::getPlayoutSecPSNR(int sec) { 00213 00214 return playsec_stat[sec % RTP_STAT_SECS].sum_psnr / 00215 playsec_stat[sec % RTP_STAT_SECS].num_psnr; 00216 00217 }; 00218 00219 00220 /*****************************************************************/ 00221 float Statistics::getStreamoutSecLossPercent(int sec) { 00222 float sentBW = (float) (getStreamoutSecDataBW(sec) 00223 + getStreamoutSecRtxBW(sec)); 00224 if (sentBW > 0) 00225 return getStreamoutSecNackedBW(sec) / (sentBW / 100); 00226 else 00227 return -1.0; 00228 } 00229 00230 /***************************************************************/ 00231 bool Statistics::checkForNewPlayoutSec(int this_frame_sec, int highest_ts_sec) { 00232 00233 if (highest_ts_sec < 0) //initial reader value is -1 !!! 00234 return true; 00235 00236 //new client buf second 00237 if ( highest_ts_sec < this_frame_sec ) { 00238 dprintf_full("Statistics(%p)::checkForNewPlayoutSec: this %i highest %i\n", 00239 this,highest_ts_sec,this_frame_sec); 00240 00241 if (this_frame_sec - highest_ts_sec > RTP_STAT_SECS) { 00242 memset(playsec_stat,0,sizeof(playsec_stat)); 00243 highest_ts_sec = this_frame_sec; 00244 } else 00245 while (highest_ts_sec < this_frame_sec ) { 00246 highest_ts_sec++; 00247 memset(&playsec_stat[highest_ts_sec % RTP_STAT_SECS],0,sizeof(OnePlayoutSecStat)); 00248 } 00249 00250 #ifdef VITOOKI_DEBUG 00251 if (this_frame_sec >= 1) { 00252 dprintf_full("Statistics(%p)::checkForNewPlayoutSec: netStat highest_ts_sec %i (last PSNR %3.3f): [data|rtx|pkts|fps]", this, highest_ts_sec, getPlayoutSecPSNR(this_frame_sec - 1)); 00253 for (int i=this_frame_sec - 1; 00254 i >= MAX(0,this_frame_sec +1 - RTP_STAT_SECS); 00255 i--) 00256 printf("#[%2i: %3i|%2i|%2i|%2i]",i, 00257 getPlayoutSecDataBW(i)/128, 00258 getPlayoutSecRtxBW(i)/128, 00259 getPlayoutSecDataPkts(i), 00260 getPlayoutSecNetFPS(i)); 00261 printf("\n"); 00262 } 00263 #endif 00264 writePlayoutSecStats(this_frame_sec); 00265 00266 return true; 00267 } 00268 return false; 00269 } 00270 00271 00272 /**************************************************************************/ 00273 void Statistics::writePlayoutSecStats(int this_frame_sec) { 00274 int oldSec = this_frame_sec - 2; 00275 00276 #ifdef VITOOKI_DEBUG 00277 00278 if (this_frame_sec >= 2) { 00279 00280 fprintf(playout_stat_fp,"%i\t", oldSec); 00281 fprintf(playout_stat_fp,"%i\t", getPlayoutSecNetFPS(oldSec)); 00282 fprintf(playout_stat_fp,"%i\t", getPlayoutSecFPS(oldSec)); //eg. after ESSync dropping 00283 fprintf(playout_stat_fp,"%i\t", getPlayoutSecDataBW(oldSec)/128); 00284 fprintf(playout_stat_fp,"%i\t", getPlayoutSecRtxBW(oldSec)/128); 00285 fprintf(playout_stat_fp,"%i\t", getPlayoutSecNackedBW(oldSec)/128); 00286 fprintf(playout_stat_fp,"%i\t", (getPlayoutSecDataBW(oldSec) 00287 + getPlayoutSecRtxBW(oldSec) 00288 - getPlayoutSecNackedBW(oldSec)) /128); 00289 fprintf(playout_stat_fp,"%3.3f\t", getPlayoutSecPSNR(oldSec)); 00290 00291 00292 fprintf(playout_stat_fp,"%i\t", getPlayoutSecIFrame(oldSec)); 00293 fprintf(playout_stat_fp,"%i\t", getPlayoutSecPFrame(oldSec)); 00294 fprintf(playout_stat_fp,"%i\t", getPlayoutSecBFrame(oldSec)); 00295 fprintf(playout_stat_fp,"%i\t", playsec_stat[oldSec % RTP_STAT_SECS].stream_switched); 00296 fprintf(playout_stat_fp,"%i\t",playsec_stat[oldSec % RTP_STAT_SECS].stream_bw/128); 00297 00298 fprintf(playout_stat_fp,"\n"); 00299 fflush(playout_stat_fp); 00300 } 00301 #endif 00302 } 00303 00304 00305 /****************************************************************/ 00306 bool Statistics::checkForNewStreamoutSec(double now_dtime) { 00307 double this_sec; 00308 u32 old_streamout_sec; 00309 int i=0; 00310 00311 if (getFirstPacketTime() == 0) { 00312 dprintf_err("Statistics::checkForNewStreamoutSec no firstPacketTime for calculations....\n"); 00313 return false; 00314 } 00315 00316 if (now_dtime < 0.0) { //no time given, so get real now time... 00317 struct timeval now_tv; 00318 gettimeofday(&now_tv,NULL); 00319 now_dtime = now_tv.tv_sec + now_tv.tv_usec/1000000.0; 00320 } 00321 00322 this_sec = now_dtime - getFirstPacketTime(); 00323 setStreamoutFloatSec(this_sec); 00324 00325 //negative playout sec for initial prefetching! 00326 setPlayoutSec(getStreamoutFloatSec() - getPrefetchedSecs()); 00327 00328 if (getStreamoutFloatSec() >= getPrefetchedSecs()) { 00329 setBufAheadSec(((double)(getHighestPacketTS()-getFirstPacketTS())/(double)es->getMediaTimeScale()) 00330 - (getStreamoutFloatSec() - getPrefetchedSecs()) 00331 - (float)getAvgRTT()/1000.0); //is always zero on client side! 00332 // conservative approach: we use RTT instead of latency, since RTT/2 might be wrong on 00333 // unevenly loaded links 00334 } else 00335 setBufAheadSec( ((double)(getHighestPacketTS()-getFirstPacketTS()) / (double)es->getMediaTimeScale()) ); 00336 00337 00338 old_streamout_sec = getStreamoutFullSec(); 00339 setStreamoutFullSec((int)float(this_sec)); 00340 00341 if (old_streamout_sec < getStreamoutFullSec()) { //NEW STREAMOUT SEC 00342 if (getStreamoutFullSec() - old_streamout_sec > RTP_STAT_SECS) { 00343 //eg. paused for over 15 secs 00344 old_streamout_sec = getStreamoutFullSec(); 00345 memset(&streamsec_stat, 0, sizeof(OneStreamoutSecStat)*RTP_STAT_SECS); 00346 } else { 00347 //reset all skipped-over secs 00348 while(old_streamout_sec < getStreamoutFullSec()) { 00349 old_streamout_sec++; //advance to next skipped one 00350 memset(&streamsec_stat[old_streamout_sec % RTP_STAT_SECS], 0, sizeof(OneStreamoutSecStat)); 00351 } 00352 } 00353 00354 streamsec_stat[(getStreamoutFullSec()-1) % RTP_STAT_SECS].stream_bw = getStreamBW(); 00355 if (getPlayoutSec() > 1) 00356 playsec_stat[((int)floor(getPlayoutSec()-1)) % RTP_STAT_SECS].stream_bw = getStreamBW(); 00357 00358 // calc client buffer fill rate 00359 //fake float part by non-exact percentage of according full sec 00360 int playout_floor_sec = MAX(0,(int)floor(getPlayoutSec())); 00361 // int buf_ahead_floor_sec = (int)floor(getBufAheadSec()); 00362 setStreamoutSecClientBufFill(getStreamoutFullSec()-1, 0); 00363 setStreamoutSecBufAhead(getStreamoutFullSec()-1, getBufAheadSec()); 00364 setStreamoutSecPlayoutSec(getStreamoutFullSec()-1, getPlayoutSec()); 00365 00366 // left-over buffers from actual playout sec 00367 double playout_fract = (double)playout_floor_sec + 1 - MAX(0.0, getPlayoutSec()); 00368 //ignore case that buf_ahead could be < 1.0, since the rest wont be in our playsec_stat anyway... 00369 incStreamoutSecClientBufFill(getStreamoutFullSec()-1, 00370 (int)( (double)(getPlayoutSecDataBW(playout_floor_sec) 00371 + getPlayoutSecRtxBW(playout_floor_sec) ) 00372 * playout_fract ) ); 00373 00374 int buf_ahead_floor_sec_left = (int)floor(getBufAheadSec() - playout_fract); 00375 for (i=1; i <= buf_ahead_floor_sec_left; i++) 00376 incStreamoutSecClientBufFill(getStreamoutFullSec()-1, 00377 getPlayoutSecDataBW(playout_floor_sec + i) 00378 + getPlayoutSecRtxBW(playout_floor_sec + i) ); 00379 00380 // and now the tail of the last sec 00381 playout_fract = getBufAheadSec() - playout_fract - buf_ahead_floor_sec_left; 00382 if (playout_fract > 0) //still more ahead 00383 incStreamoutSecClientBufFill(getStreamoutFullSec()-1, 00384 (int)((double) (getPlayoutSecDataBW( 00385 (int)floor(MAX(0,(int)getPlayoutSec()) 00386 + getBufAheadSec())) 00387 + getPlayoutSecRtxBW((int)floor(MAX(0,(int)getPlayoutSec()) 00388 + getBufAheadSec())) ) 00389 * playout_fract ) ); 00390 00391 #ifdef VITOOKI_DEBUG 00392 dprintf_full("Statistics(%p)::checkForNewStreamoutSec: netStat this_sec %6.3f" 00393 " (playout %6.3f): [data|rtx|nacked|ClBuf]", 00394 this, getStreamoutFloatSec(),getPlayoutSec()); 00395 for (i=getStreamoutFullSec() - 1; 00396 i >= MAX(0,(int)getStreamoutFullSec() + 1 - RTP_STAT_SECS); 00397 i--) 00398 printf("#[%2i: %3i|%2i|%2i|%i]",i, 00399 getStreamoutSecDataBW(i)/128, 00400 getStreamoutSecRtxBW(i)/128, 00401 getStreamoutSecNackedBW(i)/128, 00402 getStreamoutSecClientBufFill(i)/128); 00403 printf("\n"); 00404 #endif 00405 00406 return true; 00407 } 00408 00409 return false; 00410 } 00411 00412 00413 /**************************************************************************/ 00414 void Statistics::writeStreamoutSecClientStats(long preQsize) { 00415 int oldSec = getStreamoutFullSec()-1; 00416 00417 #ifdef VITOOKI_DEBUG 00418 // client-side streamout sec 00419 fprintf(streamout_stat_fp,"%i\t",oldSec); 00420 fprintf(streamout_stat_fp,"%i\t",getStreamoutSecDataBW(oldSec)/128); 00421 fprintf(streamout_stat_fp,"%i\t",getStreamoutSecRtxBW(oldSec)/128); 00422 fprintf(streamout_stat_fp,"%i\t",(getStreamoutSecDataBW(oldSec) 00423 + getStreamoutSecRtxBW(oldSec))/128); 00424 fprintf(streamout_stat_fp,"%5.3f\t",getPlayoutSec()); 00425 fprintf(streamout_stat_fp,"%5.3f\t",getBufAheadSec()); 00426 fprintf(streamout_stat_fp,"%i\t",getStreamoutSecClientBufFill(oldSec)/128); 00427 fprintf(streamout_stat_fp,"%li\t",preQsize/128); 00428 fprintf(streamout_stat_fp,"\n"); 00429 fflush(streamout_stat_fp); 00430 #endif 00431 00432 } 00433 00434 00435 /**************************************************************************/ 00436 void Statistics::writeStreamoutSecServerStats() { 00437 int oldSec = getStreamoutFullSec()-2; 00438 00439 00440 #ifdef VITOOKI_DEBUG 00441 00442 //sender-side streamout sec 00443 fprintf(streamout_stat_fp,"%i\t",oldSec); 00444 fprintf(streamout_stat_fp,"%i\t",getStreamoutSecNetBW(oldSec)/128); 00445 fprintf(streamout_stat_fp,"%i\t",getStreamoutSecDataBW(oldSec)/128); 00446 fprintf(streamout_stat_fp,"%i\t",getStreamoutSecRtxBW(oldSec)/128); 00447 fprintf(streamout_stat_fp,"%i\t",getStreamoutSecNackedBW(oldSec)/128); 00448 fprintf(streamout_stat_fp,"%i\t",(getStreamoutSecDataBW(oldSec) 00449 + getStreamoutSecRtxBW(oldSec) 00450 - getStreamoutSecNackedBW(oldSec) ) /128); 00451 fprintf(streamout_stat_fp,"%5.3f\t",getStreamoutSecNackedBW(oldSec) / // calc pkt_loss 00452 (float)((getStreamoutSecDataBW(oldSec) + getStreamoutSecRtxBW(oldSec))/100)); 00453 fprintf(streamout_stat_fp,"%5.3f\t",getPlayoutSec()); 00454 fprintf(streamout_stat_fp,"%5.3f\t",getBufAheadSec()); 00455 fprintf(streamout_stat_fp,"%i\t",getAdaptRate()); 00456 fprintf(streamout_stat_fp,"%5.0f\t",getAdaptSecs()*100); 00457 fprintf(streamout_stat_fp,"%i\t",getStreamoutSecClientBufFill(oldSec)/128); 00458 fprintf(streamout_stat_fp,"%i\t",streamsec_stat[oldSec % RTP_STAT_SECS].stream_switched); 00459 fprintf(streamout_stat_fp,"%i\t",streamsec_stat[oldSec % RTP_STAT_SECS].stream_bw/128); 00460 fprintf(streamout_stat_fp,"\n"); 00461 fflush(streamout_stat_fp); 00462 00463 #endif 00464 } 00465