MetaObject.cpp

00001 /*********************************************************************** 00002 * * 00003 * ViTooKi * 00004 * * 00005 * title: MetaObject.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 "global.hpp" 00045 #include "VideoESInfo.hpp" 00046 #include "AudioESInfo.hpp" 00047 #include "MetaObject.hpp" 00048 #include "VideoAdaptorChain.hpp" 00049 00050 00051 00052 #include "net/Url.hpp" 00053 #include "metadata/UserPreferences.hpp" 00054 00055 00060 MetaObject::MetaObject(ContainerInfo* stream) { 00061 assert(stream); 00062 url=NULL; 00063 pendingOutsideUnlock=0; 00064 lockAccess.initialize("MetaObject-lockAccess"); 00065 if(stream) { 00066 dprintf_full("MetaObject::MetaObject stream ok\n"); 00067 if (stream->getUrl()) 00068 { 00069 dprintf_full("MetaObject::MetaObject url ok\n"); 00070 url=new Url(stream->getUrl()->toString()); 00071 } 00072 variations.push_back(stream); 00073 } 00074 stream->setMetaObject(this); 00075 }; 00076 00080 MetaObject::MetaObject(const char* configFile) { 00081 loadFromConfigFile(configFile); 00082 }; 00083 00085 MetaObject::~MetaObject() { 00086 lockAccess.destroy(); 00087 delete url; 00088 00089 list<ContainerInfo*>::iterator li; 00090 while(!variations.empty()) { 00091 li=variations.begin(); 00092 delete (*li); 00093 variations.pop_front(); 00094 } 00095 }; 00096 00097 double MetaObject::calcSimpleQuality(u32 min, u32 max, u32 val) { 00098 if(min==max) { 00099 if(min==val) 00100 return 1.0; 00101 00102 return 0.0; 00103 } 00104 if(min>max) 00105 return -1.0; 00106 if(min<=val && val <=max) 00107 return (((double)val)-min)/(max-min); 00108 00109 return 0.0; 00110 } 00111 00112 00113 00132 ContainerInfo* MetaObject::findMp4Stream(u32 lowerDimX, u32 upperDimX, 00133 u32 lowerDimY, u32 upperDimY, 00134 u32 lowerBitRate, u32 upperBitRate, 00135 bool greyScale, float frameRate, 00136 bool* isExactMatch) { 00137 00138 dprintf_full("MetaObject::findMp4Stream(u32 lowX=%u,u32 uppX=%u,u32 lowY=%u,u32" 00139 " upY=%u,u32 lowBitRate=%u kbps, u32 upperBitRate=%u kbps,bool greyScale=%i)\n", 00140 lowerDimX,upperDimX,lowerDimY,upperDimY,lowerBitRate/1024,upperBitRate/1024, (int)greyScale); 00141 lockAccess.lock(); 00142 *isExactMatch=false; 00143 ContainerInfo* best=NULL; 00144 int maxHits=0; 00145 if(variations.empty()) { 00146 lockAccess.release(); 00147 return NULL; 00148 } 00149 double bestQuality=0.0, currentQuality=0.0; 00150 u32 bestBitRate; 00151 list<ContainerInfo*>::iterator li=variations.begin(); 00152 while(li!=variations.end()) { 00153 // compare 00154 VideoESInfo* temp = (*li)->getFirstVisualES(); 00155 AudioESInfo* temp2= (*li)->getFirstAudioES(); 00156 u32 sysBitRate= (temp2? temp2->getAvgBandwidth():0); 00157 sysBitRate+=(temp? temp->getAvgBandwidth():0); 00158 int countHits=0; 00159 countHits+=(sysBitRate>=lowerBitRate) ? 1 : 0; 00160 countHits+=(sysBitRate<=upperBitRate) ? 1 : 0; 00161 // problem with network: return the one version with the value 00162 // closest to the specified range, make sure this doesn't have a 00163 // too big influence in case other misses are encountered --> countHits are used for that! 00164 if(lowerBitRate<=upperBitRate) { 00165 if(lowerBitRate<=sysBitRate && sysBitRate<=upperBitRate) { 00166 currentQuality=1.0+calcSimpleQuality(lowerBitRate,upperBitRate,sysBitRate); 00167 } 00168 else { 00169 // if out of range calc a value between 0..1 stating how many % we are off 00170 if(sysBitRate < lowerBitRate && sysBitRate > lowerBitRate/2) { 00171 currentQuality=calcSimpleQuality(lowerBitRate/2,lowerBitRate,sysBitRate); 00172 } 00173 else if(sysBitRate> upperBitRate && sysBitRate < upperBitRate*2) { 00174 currentQuality=1.0-calcSimpleQuality(upperBitRate,upperBitRate*2,sysBitRate); 00175 } 00176 else 00177 currentQuality=0.0; 00178 } 00179 } 00180 else 00181 currentQuality=0.0; 00182 00183 if(temp) { 00184 if(lowerDimX<=upperDimX && lowerDimY<upperDimY && upperDimX>32 && upperDimY>32) { 00185 countHits+=(temp->getWidth()>=lowerDimX) ? 1 : 0; 00186 countHits+=(temp->getHeight()>=lowerDimY) ? 1 : 0; 00187 countHits+=(temp->getWidth()<=upperDimX) ? 1 : 0; 00188 countHits+=(temp->getHeight()<=upperDimY) ? 1 : 0; 00189 currentQuality+=calcSimpleQuality(lowerDimX,upperDimX,temp->getWidth()); 00190 currentQuality+=calcSimpleQuality(lowerDimY,upperDimY,temp->getHeight()); 00191 } 00192 else { 00193 /* ignore the incorrectly given dimension */ 00194 countHits+=4; 00195 } 00196 countHits+=(greyScale!=temp->getHasColor()) ? 1 : 0; 00197 } 00198 else 00199 countHits+=5; //dim+greyscale 00200 00201 if(frameRate>temp->getFPS()) 00202 countHits++; 00203 00204 dprintf_full("MetaObject::findMp4Stream checking on width=%u, height=%u, bitRate=%u kbps," 00205 " hasColor %i: quality %4.2f hits %i\n", 00206 temp->getWidth(),temp->getHeight(),sysBitRate/1024,temp->getHasColor(), currentQuality, countHits); 00207 00208 00209 if(countHits == 8) { 00210 // a perfect hit 00211 maxHits=countHits; 00212 *isExactMatch=true; 00213 dprintf_full("MetaObject::findMp4Stream found a perfect hit (exact match!)\r\n"); 00214 if(currentQuality > bestQuality || currentQuality <= 0.0) { 00215 best=*li; 00216 bestQuality = currentQuality; 00217 bestBitRate = sysBitRate; 00218 } 00219 } 00220 else if ((countHits >= maxHits) && (bestQuality < currentQuality) ) { 00221 // new best match 00222 best=*li; 00223 bestQuality=currentQuality; 00224 maxHits=countHits; 00225 bestBitRate = sysBitRate; 00226 } 00227 ++li; 00228 } 00229 dprintf_full("MetaObject::findMp4Stream has %i hits from 8, quality %4.2f\n",maxHits, bestQuality); 00230 lockAccess.release(); 00231 if (best) { 00232 dprintf_full("MetaObject::findMp4Stream decides on Container with width=%u, height=%u, bitRate=%i kbps firstES id %llu (url %s)\n", 00233 best->getFirstVisualES()->getWidth(),best->getFirstVisualES()->getHeight(), bestBitRate/1024, (*best->getESList()->begin())->getStreamId(), (*best->getESList()->begin())->getInput()); 00234 return best; 00235 } else 00236 return NULL; 00237 }; 00238 00242 bool MetaObject::addMP4Stream(ContainerInfo* stream) { 00243 lockAccess.lock(); 00244 00245 list<ContainerInfo*>::iterator varCI; 00246 00247 for(varCI=variations.begin(); varCI!=variations.end(); ++varCI) { 00248 // search within one variation for a similar ES entry 00249 if(stream->getUrl()->isEqual((*varCI)->getUrl())) { 00250 // compare quality! 00251 VideoESInfo* vIn=stream->getFirstVisualES(); 00252 VideoESInfo* v=(*varCI)->getFirstVisualES(); 00253 if(v->getWidth()==vIn->getWidth() && 00254 v->getAvgBandwidth()==vIn->getAvgBandwidth() && 00255 v->getFPS()>=vIn->getFPS()) { 00256 // we have same quality stored, delete the inserted one 00257 // for simplicity because the other is probably in use 00258 stream->destroy(); 00259 delete stream;stream=NULL; 00260 lockAccess.release(); 00261 return false; 00262 } 00263 } 00264 } 00265 stream->setMetaObject(this); 00266 variations.push_back(stream); 00267 lockAccess.release(); 00268 return true; 00269 00270 }; 00271 00272 bool MetaObject::deleteMP4Stream(ContainerInfo* stream) { 00273 lockAccess.lock(); 00274 if(variations.empty()) 00275 return false; 00276 00277 list<ContainerInfo*>::iterator start,end; 00278 start=variations.begin(); 00279 end=variations.end(); 00280 bool found=false; 00281 while(start!=end) { 00282 if(stream == (*start) ) 00283 found=true; 00284 if( !found && (*start)->getLocalFile() && stream->getLocalFile() 00285 && strcmp((*start)->getLocalFile(),stream->getLocalFile())==0) 00286 found=true; 00287 if(!found && (*start)->getFirstVisualES() && 00288 (*start)->getFirstVisualES()->getInput() && 00289 stream->getFirstVisualES()->getInput() && 00290 strcmp((*start)->getFirstVisualES()->getInput(),stream->getFirstVisualES()->getInput()) == 0) 00291 found=true; 00292 if(!found && (*start)->getFirstAudioES() && 00293 (*start)->getFirstAudioES()->getInput() && 00294 stream->getFirstAudioES()->getInput() && 00295 strcmp((*start)->getFirstAudioES()->getInput(),stream->getFirstAudioES()->getInput()) == 0) 00296 found=true; 00297 if(found) { 00298 delete (*start); 00299 variations.erase(start); 00300 lockAccess.release(); 00301 return true; 00302 } 00303 ++start; 00304 } 00305 lockAccess.release(); 00306 return false; 00307 }; 00308 00309 bool MetaObject::saveToConfigFile(const char* cfg) { 00310 lockAccess.lock(); 00311 00312 if(variations.empty() || !cfg || !this->url) { 00313 lockAccess.release(); 00314 return false; 00315 } 00316 FILE* fp=fopen(cfg,"wb"); 00317 bool retVal=false; 00318 char* pCfg=strrchr(cfg,'.'); 00319 char *temp = new char[1024]; 00320 if(pCfg) { 00321 strncpy(temp,cfg,pCfg-cfg-1); //do not copy the dot 00322 temp[pCfg-cfg-1]=0; 00323 pCfg=&temp[pCfg-cfg-1]; 00324 } 00325 else { 00326 strcpy(temp,cfg); 00327 pCfg=&temp[strlen(temp)]; 00328 } 00329 // pCfg is used for appending XX.conf 00330 if(fp) { 00331 char pCint[10]; 00332 retVal=true; 00333 int i=0; 00334 fprintf(fp,"%s\n",this->url->toString()); 00335 list<ContainerInfo*>::iterator start=variations.begin(); 00336 while(start!=variations.end()) { 00337 sprintf(pCint,"%2d",i); 00338 strcpy(pCfg,pCint); 00339 strcat(pCfg,".conf"); 00340 retVal&=(*start)->saveToCfgFile(temp); 00341 if(retVal) { 00342 fprintf(fp,"%s\n",temp); 00343 } 00344 ++start; 00345 i++; 00346 } 00347 fclose(fp); 00348 } 00349 lockAccess.release(); 00350 delete [] temp; 00351 return retVal; 00352 }; 00353 00354 bool MetaObject::loadFromConfigFile(const char* cfg) { 00355 FILE* fp=fopen(cfg,"rb"); 00356 if(!fp) 00357 return false; 00358 00359 lockAccess.lock(); 00360 if(url) { 00361 delete url; 00362 } 00363 00364 list<ContainerInfo*>::iterator start; 00365 while(!variations.empty()) { 00366 start=variations.begin(); 00367 delete (*start); 00368 variations.pop_front(); 00369 } 00370 00371 // everything cleaned up 00372 bool retVal=true; 00373 char *line = new char[1024]; 00374 fgets(line,1024,fp); 00375 // first line contains url 00376 url=new Url(line); 00377 00378 // then read the objects 00379 ContainerInfo* info=NULL; 00380 //int counter=0; 00381 while(!feof(fp)) { 00382 fgets(line,1024,fp); 00383 info=new ContainerInfo(); 00384 if(info->loadFromCfgFile(line)) { 00385 variations.push_back(info); 00386 } 00387 else 00388 retVal=false; 00389 } 00390 fclose(fp); 00391 lockAccess.release(); 00392 delete [] line; 00393 return retVal; 00394 }; 00395 00396 std::list<ContainerInfo*>* MetaObject::getVariations() 00397 { 00398 lockAccess.lock(); 00399 pendingOutsideUnlock++; 00400 return &variations; 00401 }; 00402 00403 void MetaObject::releaseVariationsLock() 00404 { 00405 if(pendingOutsideUnlock>0) { 00406 pendingOutsideUnlock--; 00407 lockAccess.release(); 00408 } 00409 }