AdmissionControl.cpp

00001 /*********************************************************************** 00002 * * 00003 * ViTooKi * 00004 * * 00005 * title: AdmissionControl.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 "AdmissionControl.hpp" 00045 #include "VideoESInfo.hpp" 00046 #include "AudioESInfo.hpp" 00047 #include "adaptors/Forwarder.hpp" 00048 #include "adaptors/MP4Encoder.hpp" 00049 #include "adaptors/MP4Decoder.hpp" 00050 #include "adaptors/TemporalAdaptor.hpp" 00051 #include "adaptors/YUVColorReductionAdaptor.hpp" 00052 #include "adaptors/YUVSpatialReductionAdaptor.hpp" 00053 #include "MetaObject.hpp" 00054 #include "VideoAdaptorChain.hpp" 00055 #include "net/ProxySession.hpp" 00056 00057 AdmissionControl::AdmissionControl(const ResourceLimit* lim,const ResourcePrices* p):currentUsage() 00058 { 00059 assert(lim); 00060 assert(p); 00061 limit=new ResourceLimit(lim); 00062 prices=new ResourcePrices(p); 00063 activeRequests=0; 00064 lock.initialize("AdmissionControl-lock"); 00065 }; 00066 AdmissionControl::~AdmissionControl() 00067 { 00068 if(limit) 00069 delete limit; 00070 if(prices) 00071 delete prices; 00072 }; 00074 bool AdmissionControl::canAdmitRequest(const ResourceUsage* usage) 00075 { 00076 return (currentUsage.cpu+usage->cpu<1.0) && 00077 (currentUsage.network+usage->network<1.0) && 00078 (currentUsage.disk+usage->disk<1.0); 00079 } 00080 00082 bool AdmissionControl::insertRequest(const ResourceUsage* usage) 00083 { 00084 assert(usage); 00085 bool ret=false; 00086 dprintf_full("AdmissionControl::insertRequest(N=%f,C=%f,H=%f)\n",usage->network,usage->cpu,usage->disk); 00087 if(usage->cpu==usage->disk && usage->disk==usage->network && usage->network<0.00000001) { 00088 // ignore zero req, don't change activeRequests 00089 return true; 00090 } 00091 dprintf_full("AdmissionControl::insertRequest act %i ((N=%f,C=%f,H=%f)\n", 00092 activeRequests,currentUsage.network,currentUsage.cpu,currentUsage.disk); 00093 00094 lock.lock(); 00095 if(activeRequests==0) { 00096 // reset resourceusage to 0 to deal with fp rounding errors 00097 // which otherwise would accumulate 00098 currentUsage.cpu=currentUsage.network=currentUsage.disk=0.0; 00099 } 00100 if(canAdmitRequest(usage)) { 00101 currentUsage.add(usage); 00102 activeRequests++; 00103 dprintf_full("AdmissionControl::insertRequest: INSERTED\n"); 00104 ret=true; 00105 } 00106 else 00107 prxStat.rejected++; 00108 lock.release(); 00109 return ret; 00110 } 00111 00113 bool AdmissionControl::freeRequest(const ResourceUsage* usage) 00114 { 00115 assert(usage); 00116 bool ret=false; 00117 dprintf_full("AdmissionControl::freeRequest(N=%f,C=%f,H=%f)\n",usage->network,usage->cpu,usage->disk); 00118 lock.lock(); 00119 if(usage->cpu==usage->disk && usage->disk==usage->network && usage->network<0.00000001) { 00120 // ignore zero req, don't change activeRequests 00121 ret=true; 00122 } 00123 else if(activeRequests>0) { 00124 // at least one request is open! 00125 ret=true; 00126 currentUsage.subtract(usage); 00127 dprintf_full("AdmissionControl::freeRequest: SUCCESS \n"); 00128 activeRequests--; 00129 } 00130 lock.release(); 00131 return ret; 00132 } 00133 00138 Adaptor* AdmissionControl::makeExactMatch(unsigned int dimX, 00139 unsigned int dimY, 00140 unsigned int bitRate, 00141 bool greyScale, float frameRate, 00142 ContainerInfo* stream,ResourceUsage* ru, 00143 bool allowHigherThanSrcBitRate) { 00144 dprintf_full("AdmissionControl::makeExactMatch IN: dimx %u dimy %u bitRate %u grey %i\r\n", 00145 dimX, dimY, bitRate, greyScale); 00146 assert(ru); 00147 00148 if (!stream) 00149 return NULL; 00150 // doesn't access variations, no need to lock 00151 VideoESInfo* es= stream->getFirstVisualES(); 00152 AudioESInfo* es2=stream->getFirstAudioES(); 00153 if(es2) 00154 bitRate-=es2->getAvgBandwidth(); 00155 00156 if(!es) { 00157 dprintf_err("AdmissionControl::makeExactMatch: Video with no Visual ES?\r\n"); 00158 ru->cpu=ru->disk=ru->network=0.0; 00159 return new Forwarder(); 00160 } 00161 00162 // we have a cache hit! the source is always read from the disk 00163 Feature src(es); 00164 const char* fileTmp=es->getInput(); 00165 if(fileTmp) { 00166 FILE* fpTmp=fopen(fileTmp,"rb"); 00167 if(fpTmp) { 00168 src.isCached=true; 00169 fclose(fpTmp); 00170 } 00171 else 00172 src.isCached=false; 00173 } 00174 else 00175 src.isCached=false; 00176 00177 Feature dst(es); 00178 dst.isCached=false; 00179 dst.bitrate=bitRate; 00180 dst.dimX=dimX; 00181 dst.dimY=dimY; 00182 dst.color=!greyScale; 00183 dst.frameRate=frameRate; 00184 ResourceUsage* ruTmp=CostFunction::calcResourceUsage(&src,&dst,limit); 00185 00186 ru->network=ruTmp->network; 00187 ru->disk=ruTmp->disk; 00188 ru->cpu=ruTmp->cpu; 00189 delete ruTmp; 00190 // CPU is calculated on the adaptor! 00191 VideoAdaptorChain* result=NULL; 00192 // FIXME:? no upscaling support!!!! 00193 if(es->getWidth()<dimX) 00194 dimX=es->getWidth(); 00195 if(es->getHeight()<dimY) 00196 dimY=es->getHeight(); 00197 bool transcode=false; 00198 int adaptorCount=0; 00199 if( (es->getWidth()/8) != (dimX/8) || 00200 00201 (es->getHeight()/8) != (dimY/8) || greyScale 00202 00203 || bitRate < es->getAvgBandwidth() || es->getFPS()>frameRate) { 00204 00205 // we need transcoding!!! 00206 00207 transcode=true; 00208 00209 result=new VideoAdaptorChain(); 00210 00211 } 00212 if(!transcode && allowHigherThanSrcBitRate && bitRate!=es->getAvgBandwidth()) { 00213 transcode=true; 00214 result=new VideoAdaptorChain(); 00215 } 00216 if(es->getFPS()>frameRate) 00217 result->addAdaptor(new TemporalAdaptor(es)); 00218 if(transcode) { 00219 //ATTENTION: make sure to use the same dec/enc lib, so either both xvid or both ffmpeg!! 00220 result->addAdaptor(new MP4Decoder(es, false, UncompressedVideoFrame::ColorSpaceYV12, MP4Decoder::FFMPEG)); 00221 adaptorCount++; 00222 } 00223 00224 if( (es->getWidth()/8) != (dimX/8) || 00225 (es->getHeight()/8) != (dimY/8) ) { 00226 // it's up to the user to decide if he wants to keep 00227 // the aspect ratio, we stick to dimX and dimY 00228 result->addAdaptor(new YUVSpatialReductionAdaptor(es, &dimX, &dimY)); 00229 adaptorCount++; 00230 } 00231 if(greyScale) { 00232 result->addAdaptor(new YUVColorReductionAdaptor(es)); 00233 adaptorCount++; 00234 } 00235 if (bitRate > es->getAvgBandwidth() && !allowHigherThanSrcBitRate) { 00236 bitRate=es->getAvgBandwidth(); 00237 } 00238 if(transcode) { 00239 u32 new_bitrate = dimX * dimY * 6; 00240 if(!allowHigherThanSrcBitRate) { 00241 if (new_bitrate > bitRate) 00242 new_bitrate = bitRate; 00243 } 00244 else 00245 new_bitrate = bitRate; 00246 result->addAdaptor(new MP4Encoder(es, new_bitrate, (int)es->getFPS(), 300 /*GOP size*/, 0 /*BVOPs*/, 00247 UncompressedVideoFrame::ColorSpaceYV12, MP4Decoder::FFMPEG)); 00248 result->initialize(); // must be called prior cost calc 00249 // fix cpu to correct value 00250 ru->cpu= ((double)result->getTranscodingCosts())/limit->cpu; 00251 adaptorCount++; 00252 } 00253 else { 00254 assert(result==NULL); 00255 dprintf_small("MetaObject::makeExactMatch created a Forwarder\n"); 00256 ru->cpu=0.0; 00257 if(result) 00258 delete result; 00259 if(!insertRequest(ru)) { 00260 ru->network=ru->disk=0.0; //rejected 00261 } 00262 return new Forwarder(); 00263 } 00264 dprintf_small("AdmissionControl::makeExactMatch created AdaptorChain of size %i\n",adaptorCount); 00265 if(!insertRequest(ru)) { 00266 ru->network=ru->disk=ru->cpu=0.0; //rejected 00267 delete result;result=NULL; 00268 } 00269 return result; 00270 }; 00271 00276 Adaptor* AdmissionControl::makeBestMatch(const UserPreferences* up, 00277 int delayToServer, 00278 MetaObject* meta, 00279 /* out parameters */ 00280 ContainerInfo** ci, ResourceUsage* ru) 00281 { 00282 assert(up && ci && meta && ru); 00283 ResourceUsage resUse; 00284 bool originalBest=false; 00285 static const int bitrates[]={1,2,4,8}; 00286 static const int nrBR=4; 00287 00288 int dimx_min=((up->dimX.min+43)/44)*44; 00289 int dimx_max=(up->dimX.max/44)*44; 00290 Video* target=NULL; 00291 double fr_min=up->frameRate.min; 00292 double fr_max=up->frameRate.max; 00293 double result,quality,costs; 00294 Video* bestTarget=NULL; 00295 double bestResultCosts=FLT_MAX; 00296 00297 ContainerInfo* bestVideo=NULL; 00298 // always check the original videos first! 00299 int realDelay=0; 00300 ResourceUsage* job=NULL; 00301 // locks the meta lock!!!! 00302 std::list<ContainerInfo*>* variations=meta->getVariations(); 00303 std::list<ContainerInfo*>::iterator origVid; 00304 for(origVid=variations->begin();origVid!=variations->end();++origVid) { 00305 // all videos stored in variations are hits (more or less :-)) 00306 Feature sourceVideo( (*origVid)->getFirstVisualES()); 00307 const char* fileTmp=(*origVid)->getFirstVisualES()->getInput(); 00308 if(fileTmp) { 00309 FILE* fpTmp=fopen(fileTmp,"rb"); 00310 if(fpTmp) { 00311 sourceVideo.isCached=true; 00312 fclose(fpTmp); 00313 } 00314 else 00315 sourceVideo.isCached=false; 00316 } 00317 else 00318 sourceVideo.isCached=false; 00319 00320 job=CostFunction::calcResourceUsage( &sourceVideo, &sourceVideo, limit); 00321 costs=CostFunction::calcWeightedMonetaryCosts(&currentUsage,job,prices, 00322 CostFunction::baseCosts,sourceVideo.contentCosts,realDelay,up->delayInMS.point); 00323 quality=up->calcQuality(&sourceVideo,realDelay); 00324 if(quality<0.0001) 00325 result=FLT_MAX; 00326 else if(costs+CostFunction::baseCosts+sourceVideo.contentCosts>up->pay) 00327 result=FLT_MAX; 00328 else 00329 result=CostFunction::calcFinalCosts(quality,up->pay,costs); 00330 // minimize transcoding were possible, otherwise check here for full hits! 00331 if(quality>0.01) { 00332 if(result<bestResultCosts) { 00333 bestResultCosts=result; 00334 bestVideo=(*origVid); 00335 ru->cpu=job->cpu;ru->network=job->network;ru->disk=job->disk; 00336 } 00337 } 00338 delete job;job=NULL; 00339 } 00340 if(bestVideo) { 00341 *ci=bestVideo; 00342 meta->releaseVariationsLock(); 00343 if(!insertRequest(ru)) { 00344 ru->cpu=ru->network=ru->disk=0.0; 00345 } 00346 return NULL; 00347 } 00348 00349 originalBest=false; 00350 // if we come that far we have to do adaptation 00351 for(origVid=variations->begin();origVid!=variations->end();++origVid) { 00352 // all videos stored in variations are hits (more or less :-)) 00353 Feature sourceVideo( (*origVid)->getFirstVisualES()); 00354 const char* fileTmp=(*origVid)->getFirstVisualES()->getInput(); 00355 if(fileTmp) { 00356 FILE* fpTmp=fopen(fileTmp,"rb"); 00357 if(fpTmp) { 00358 sourceVideo.isCached=true; 00359 fclose(fpTmp); 00360 } 00361 else 00362 sourceVideo.isCached=false; 00363 } 00364 else 00365 sourceVideo.isCached=false; 00366 00367 for(int dimx=dimx_min;dimx<=dimx_max;dimx+=44) { 00368 int br_min_pos=0; 00369 int nrPixels=(int)(dimx*(((double)dimx)/sourceVideo.getAspectRatio())); 00370 00371 while(up->bitrate.min>(nrPixels*bitrates[br_min_pos]) && br_min_pos<nrBR) 00372 br_min_pos++; 00373 00374 int br_max_pos=nrBR-1; 00375 while(up->bitrate.max<(nrPixels*bitrates[br_max_pos]) && br_min_pos<br_max_pos) 00376 br_max_pos--; 00377 for(int brPos=br_min_pos;brPos<=br_max_pos;brPos++) { 00378 int br=bitrates[brPos]*nrPixels; 00379 for(double fr=fr_min;fr<=fr_max+0.001;fr+=5.0) { 00380 // assume not cached 00381 // assume that content cost degrade lineary with dimX 00382 int ccost=(int)(((double)dimx)/sourceVideo.dimX*sourceVideo.contentCosts); 00383 target=new Video(dimx,(int)(((double)dimx)/sourceVideo.getAspectRatio()), 00384 up->color.point,br,0,ccost,fr,sourceVideo.durationInSec); 00385 // assume startup delay of 1 second 00386 // result=CostFunction::calcFinalCosts(req,target,r,systemLoad,prices,delayToServer); 00387 job=CostFunction::calcResourceUsage( &sourceVideo, target, limit); 00388 costs=CostFunction::calcWeightedMonetaryCosts(&currentUsage,job,prices, 00389 CostFunction::baseCosts,target->contentCosts,delayToServer,up->delayInMS.point); 00390 quality=up->calcQuality(target,delayToServer); 00391 if(quality<0.0001) 00392 result=FLT_MAX; 00393 else if(costs+CostFunction::baseCosts+ccost>up->pay) 00394 result=FLT_MAX; 00395 else 00396 result=CostFunction::calcFinalCosts(quality,up->pay,costs); 00397 00398 if(result<bestResultCosts && job) { 00399 if(bestTarget) 00400 delete bestTarget; 00401 bestTarget=target; 00402 // frame dropping hack: when dropping B-frames never store the adapted version in the cache 00403 // store the original one, detect this case by checking cpu usage of the task 00404 if(job->cpu<0.00001) 00405 printf("Detected TEMPORAL ADAPTATION\r\n"); 00406 originalBest=(job->cpu<0.00001); 00407 //*originalBest=false; 00408 bestResultCosts=result; 00409 bestVideo=(*origVid); 00410 target=NULL; 00411 ru->cpu=job->cpu;ru->network=job->network;ru->disk=job->disk; 00412 } 00413 if(target) { 00414 delete target; 00415 target=NULL; 00416 } 00417 if(job) { 00418 delete job; 00419 job=NULL; 00420 } 00421 } 00422 } 00423 } 00424 } 00425 00426 if(target) { 00427 delete target; 00428 target=NULL; 00429 } 00430 if(job) { 00431 delete job; 00432 job=NULL; 00433 } 00434 if(bestVideo) { 00435 // make adaptorchain for bestVideo --> bestTarget 00436 // makeexact also has already inserted the costs for ru! 00437 Adaptor* ada=AdmissionControl::makeExactMatch(bestTarget->dimX,bestTarget->dimY, 00438 bestTarget->bitrate,!bestTarget->color,(float)bestTarget->frameRate,bestVideo,ru,true); 00439 // calculate costs 00440 meta->releaseVariationsLock(); 00441 *ci=bestVideo; 00442 if(bestTarget) 00443 delete bestTarget; 00444 return ada; 00445 } 00446 meta->releaseVariationsLock(); 00447 *ci=NULL; 00448 ru->cpu=ru->network=ru->disk=0.0; 00449 return NULL; 00450 };