00001
00002 #include <stdlib.h>
00003
00004 #ifndef USING_MINGW // dlfcn for mingw defined in compat.h
00005 #include <dlfcn.h>
00006 #else
00007 #include "compat.h"
00008 #endif
00009
00010
00011 #include <QDir>
00012 #include <QStringList>
00013
00014
00015 #include "mythcontext.h"
00016 #include "filtermanager.h"
00017 #include "mythdirs.h"
00018
00019 #define LOC QString("FilterManager: ")
00020
00021 static const char *FmtToString(VideoFrameType ft)
00022 {
00023 switch(ft)
00024 {
00025 case FMT_NONE:
00026 return "NONE";
00027 case FMT_RGB24:
00028 return "RGB24";
00029 case FMT_YV12:
00030 return "YV12";
00031 case FMT_ARGB32:
00032 return "ARGB32";
00033 case FMT_YUV422P:
00034 return "YUV422P";
00035 default:
00036 return "INVALID";
00037 }
00038 }
00039
00040 FilterChain::~FilterChain()
00041 {
00042 vector<VideoFilter*>::iterator it = filters.begin();
00043 for (; it != filters.end(); ++it)
00044 {
00045 VideoFilter *filter = *it;
00046 if (filter->opts)
00047 free(filter->opts);
00048 if (filter->cleanup)
00049 filter->cleanup(filter);
00050 dlclose(filter->handle);
00051 free(filter);
00052 }
00053 filters.clear();
00054 }
00055
00056 void FilterChain::ProcessFrame(VideoFrame *frame, FrameScanType scan)
00057 {
00058 if (!frame)
00059 return;
00060
00061 vector<VideoFilter*>::iterator it = filters.begin();
00062 for (; it != filters.end(); ++it)
00063 (*it)->filter(*it, frame, kScan_Intr2ndField == scan);
00064 }
00065
00066 FilterManager::FilterManager()
00067 {
00068 QDir FiltDir(GetFiltersDir());
00069
00070 FiltDir.setFilter(QDir::Files | QDir::Readable);
00071 if (FiltDir.exists())
00072 {
00073 QStringList LibList = FiltDir.entryList();
00074 for (QStringList::iterator i = LibList.begin(); i != LibList.end();
00075 ++i)
00076 {
00077 QString path = FiltDir.filePath(*i);
00078 if (path.length() <= 1)
00079 continue;
00080
00081 LOG(VB_PLAYBACK | VB_FILE, LOG_INFO, LOC +
00082 QString("Loading filter '%1'").arg(path));
00083
00084 if (!LoadFilterLib(path))
00085 {
00086 LOG(VB_GENERAL, LOG_WARNING, LOC +
00087 QString("Failed to load filter library: %1").arg(path));
00088 }
00089 }
00090 }
00091 else
00092 LOG(VB_GENERAL, LOG_ERR,
00093 "Filter dir '" + FiltDir.absolutePath() + "' doesn't exist?");
00094 }
00095
00096 FilterManager::~FilterManager()
00097 {
00098 filter_map_t::iterator itf = filters.begin();
00099 for (; itf != filters.end(); ++itf)
00100 {
00101 FilterInfo *tmp = itf->second;
00102 itf->second = NULL;
00103
00104 free(tmp->name);
00105 free(tmp->descript);
00106 free(tmp->libname);
00107 delete [] (tmp->formats);
00108 delete tmp;
00109 }
00110 filters.clear();
00111
00112 library_map_t::iterator ith = dlhandles.begin();
00113 for (; ith != dlhandles.end(); ++ith)
00114 {
00115 void *tmp = ith->second;
00116 ith->second = NULL;
00117 dlclose(tmp);
00118 }
00119 dlhandles.clear();
00120 }
00121
00122 bool FilterManager::LoadFilterLib(const QString &path)
00123 {
00124 dlerror();
00125
00126 void *dlhandle = NULL;
00127 library_map_t::iterator it = dlhandles.find(path);
00128 if (it != dlhandles.end())
00129 dlhandle = it->second;
00130
00131 if (!dlhandle)
00132 {
00133 QByteArray apath = path.toAscii();
00134 dlhandle = dlopen(apath.constData(), RTLD_LAZY);
00135 if (!dlhandle)
00136 {
00137 const char *errmsg = dlerror();
00138 LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to load filter library: " +
00139 QString("'%1'").arg(path) + "\n\t\t\t" + errmsg);
00140 return false;
00141 }
00142 dlhandles[path] = dlhandle;
00143 }
00144
00145 const ConstFilterInfo *filtInfo = (const ConstFilterInfo*)
00146 dlsym(dlhandle, "filter_table");
00147
00148 if (!filtInfo)
00149 {
00150 const char *errmsg = dlerror();
00151 LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to load filter symbol: " +
00152 QString("'%1'").arg(path) + "\n\t\t\t" + errmsg);
00153 return false;
00154 }
00155
00156 for (; filtInfo->filter_init; filtInfo++)
00157 {
00158 if (!filtInfo->filter_init || !filtInfo->name || !filtInfo->formats)
00159 break;
00160
00161 FilterInfo *newFilter = new FilterInfo;
00162 newFilter->filter_init = NULL;
00163 newFilter->name = strdup(filtInfo->name);
00164 newFilter->descript = strdup(filtInfo->descript);
00165
00166 int i = 0;
00167 for (; filtInfo->formats[i].in != FMT_NONE; i++);
00168
00169 newFilter->formats = new FmtConv[i + 1];
00170 memcpy(newFilter->formats, filtInfo->formats,
00171 sizeof(FmtConv) * (i + 1));
00172
00173 QByteArray libname = path.toAscii();
00174 newFilter->libname = strdup(libname.constData());
00175 filters[newFilter->name] = newFilter;
00176 LOG(VB_PLAYBACK, LOG_DEBUG, LOC + QString("filters[%1] = 0x%2")
00177 .arg(newFilter->name).arg((uint64_t)newFilter,0,16));
00178 }
00179 return true;
00180 }
00181
00182 const FilterInfo *FilterManager::GetFilterInfo(const QString &name) const
00183 {
00184 const FilterInfo *finfo = NULL;
00185 filter_map_t::const_iterator it = filters.find(name);
00186 if (it != filters.end())
00187 finfo = it->second;
00188
00189 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("GetFilterInfo(%1)").arg(name) +
00190 QString(" returning: 0x%1").arg((uint64_t)finfo,0,16));
00191
00192 return finfo;
00193 }
00194
00195 FilterChain *FilterManager::LoadFilters(QString Filters,
00196 VideoFrameType &inpixfmt,
00197 VideoFrameType &outpixfmt, int &width,
00198 int &height, int &bufsize,
00199 int max_threads)
00200 {
00201 if (Filters.toLower() == "none")
00202 return NULL;
00203
00204 vector<const FilterInfo*> FiltInfoChain;
00205 FilterChain *FiltChain = new FilterChain;
00206 vector<FmtConv*> FmtList;
00207 const FilterInfo *FI;
00208 const FilterInfo *FI2;
00209 QString Opts;
00210 const FilterInfo *Convert = GetFilterInfo("convert");
00211 QStringList OptsList;
00212 QStringList FilterList = Filters.split(",", QString::SkipEmptyParts);
00213 VideoFilter *NewFilt = NULL;
00214 FmtConv *FC, *FC2, *S1, *S2, *S3;
00215 VideoFrameType ifmt;
00216 unsigned int i;
00217 int nbufsize;
00218 int cbufsize;
00219 int postfilt_width = width;
00220 int postfilt_height = height;
00221
00222 for (QStringList::Iterator i = FilterList.begin();
00223 i != FilterList.end(); ++i)
00224 {
00225 QString FiltName = (*i).section('=', 0, 0);
00226 QString FiltOpts = (*i).section('=', 1);
00227
00228 if (FiltName.contains("opengl", Qt::CaseInsensitive) ||
00229 FiltName.contains("vdpau", Qt::CaseInsensitive))
00230 continue;
00231
00232 FI = GetFilterInfo(FiltName);
00233
00234 if (FI)
00235 {
00236 FiltInfoChain.push_back(FI);
00237 OptsList.push_back(FiltOpts);
00238 }
00239 else
00240 {
00241 LOG(VB_GENERAL, LOG_ERR, LOC +
00242 QString("Failed to load filter '%1', "
00243 "no such filter exists").arg(FiltName));
00244 FiltInfoChain.clear();
00245 break;
00246 }
00247 }
00248
00249 ifmt = inpixfmt;
00250 for (i = 0; i < FiltInfoChain.size(); i++)
00251 {
00252 S1 = S2 = S3 = NULL;
00253 FI = FiltInfoChain[i];
00254 if (FiltInfoChain.size() - i == 1)
00255 {
00256 for (FC = FI->formats; FC->in != FMT_NONE; FC++)
00257 {
00258 if (FC->out == outpixfmt && FC->in == ifmt)
00259 {
00260 S1 = FC;
00261 break;
00262 }
00263 if (FC->in == ifmt && !S2)
00264 S2 = FC;
00265 if (FC->out == outpixfmt && !S3)
00266 S3 = FC;
00267 }
00268 }
00269 else
00270 {
00271 FI2 = FiltInfoChain[i+1];
00272 for (FC = FI->formats; FC->in != FMT_NONE; FC++)
00273 {
00274 for (FC2 = FI2->formats; FC2->in != FMT_NONE; FC2++)
00275 {
00276 if (FC->in == ifmt && FC->out == FC2->in)
00277 {
00278 S1 = FC;
00279 break;
00280 }
00281 if (FC->out == FC2->in && !S3)
00282 S3 = FC;
00283 }
00284 if (S1)
00285 break;
00286 if (FC->in == ifmt && !S2)
00287 S2 = FC;
00288 }
00289 }
00290
00291 if (S1)
00292 FC = S1;
00293 else if (S2)
00294 FC = S2;
00295 else if (S3)
00296 FC = S3;
00297 else
00298 FC = FI->formats;
00299
00300 if (FC->in != ifmt && (i > 0 || ifmt != FMT_NONE))
00301 {
00302 if (!Convert)
00303 {
00304 LOG(VB_GENERAL, LOG_ERR, "FilterManager: format conversion "
00305 "needed but convert filter not found");
00306 FiltInfoChain.clear();
00307 break;
00308 }
00309 FiltInfoChain.insert(FiltInfoChain.begin() + i, Convert);
00310 OptsList.insert(i, QString ());
00311 FmtList.push_back(new FmtConv);
00312 if (FmtList.back())
00313 {
00314 FmtList.back()->in = ifmt;
00315 FmtList.back()->out = FC->in;
00316 i++;
00317 }
00318 else
00319 {
00320 LOG(VB_GENERAL, LOG_ERR,
00321 "FilterManager: memory allocation "
00322 "failure, returning empty filter chain");
00323 FiltInfoChain.clear();
00324 break;
00325 }
00326 }
00327 FmtList.push_back(new FmtConv);
00328 if (FmtList.back())
00329 {
00330 FmtList.back()->in = FC->in;
00331 FmtList.back()->out = FC->out;
00332 }
00333 else
00334 {
00335 LOG(VB_GENERAL, LOG_ERR,
00336 "FilterManager: memory allocation failure, "
00337 "returning empty filter chain");
00338 FiltInfoChain.clear();
00339 break;
00340 }
00341 ifmt = FC->out;
00342 }
00343
00344 if (ifmt != outpixfmt && outpixfmt != FMT_NONE &&
00345 (FiltInfoChain.size() || inpixfmt != FMT_NONE))
00346 {
00347 if (!Convert)
00348 {
00349 LOG(VB_GENERAL, LOG_ERR, "FilterManager: format conversion "
00350 "needed but convert filter not found");
00351 FiltInfoChain.clear();
00352 }
00353 else
00354 {
00355 FiltInfoChain.push_back(Convert);
00356 OptsList.push_back( QString ());
00357 FmtList.push_back(new FmtConv);
00358 if (FmtList.back())
00359 {
00360 FmtList.back()->in = ifmt;
00361 FmtList.back()->out = outpixfmt;
00362 }
00363 else
00364 {
00365 LOG(VB_GENERAL, LOG_ERR,
00366 "FilterManager: memory allocation "
00367 "failure, returning empty filter chain");
00368 FiltInfoChain.clear();
00369 }
00370 }
00371 }
00372
00373 nbufsize = -1;
00374
00375 if (FiltInfoChain.empty())
00376 {
00377 delete FiltChain;
00378 FiltChain = NULL;
00379 }
00380
00381 for (i = 0; i < FiltInfoChain.size(); i++)
00382 {
00383 QByteArray tmp = OptsList[i].toLocal8Bit();
00384 NewFilt = LoadFilter(FiltInfoChain[i], FmtList[i]->in,
00385 FmtList[i]->out, postfilt_width,
00386 postfilt_height, tmp.constData(),
00387 max_threads);
00388
00389 if (!NewFilt)
00390 {
00391 delete FiltChain;
00392 LOG(VB_GENERAL, LOG_ERR, QString("FilterManager: failed to load "
00393 "filter %1 %2->%3 with args %4")
00394 .arg(FiltInfoChain[i]->name)
00395 .arg(FmtToString(FmtList[i]->in))
00396 .arg(FmtToString(FmtList[i]->out))
00397 .arg(OptsList[i]));
00398 FiltChain = NULL;
00399 nbufsize = -1;
00400 break;
00401 }
00402
00403 if (NewFilt->filter && FiltChain)
00404 {
00405 FiltChain->Append(NewFilt);
00406 }
00407 else
00408 {
00409 if (NewFilt->opts)
00410 free(NewFilt->opts);
00411 if (NewFilt->cleanup)
00412 NewFilt->cleanup(NewFilt);
00413 dlclose(NewFilt->handle);
00414 free(NewFilt);
00415 }
00416
00417 switch (FmtList[i]->out)
00418 {
00419 case FMT_YV12:
00420 cbufsize = postfilt_width * postfilt_height * 3 / 2;
00421 break;
00422 case FMT_YUV422P:
00423 cbufsize = postfilt_width * postfilt_height * 2;
00424 break;
00425 case FMT_RGB24:
00426 cbufsize = postfilt_width * postfilt_height * 3;
00427 break;
00428 case FMT_ARGB32:
00429 cbufsize = postfilt_width * postfilt_height * 4;
00430 break;
00431 default:
00432 cbufsize = 0;
00433 }
00434
00435 if (cbufsize > nbufsize)
00436 nbufsize = cbufsize;
00437 }
00438
00439 if (FiltChain)
00440 {
00441 if (inpixfmt == FMT_NONE)
00442 inpixfmt = FmtList.front()->in;
00443 if (outpixfmt == FMT_NONE)
00444 inpixfmt = FmtList.back()->out;
00445 width = postfilt_width;
00446 height = postfilt_height;
00447 }
00448 else
00449 {
00450 if (inpixfmt == FMT_NONE && outpixfmt == FMT_NONE)
00451 inpixfmt = outpixfmt = FMT_YV12;
00452 else if (inpixfmt == FMT_NONE)
00453 inpixfmt = outpixfmt;
00454 else if (outpixfmt == FMT_NONE)
00455 outpixfmt = inpixfmt;
00456 }
00457
00458 switch (inpixfmt)
00459 {
00460 case FMT_YV12:
00461 cbufsize = postfilt_width * postfilt_height * 3 / 2;
00462 break;
00463 case FMT_YUV422P:
00464 cbufsize = postfilt_width * postfilt_height * 2;
00465 break;
00466 case FMT_RGB24:
00467 cbufsize = postfilt_width * postfilt_height * 3;
00468 break;
00469 case FMT_ARGB32:
00470 cbufsize = postfilt_width * postfilt_height * 4;
00471 break;
00472 default:
00473 cbufsize = 0;
00474 }
00475
00476 if (cbufsize > nbufsize)
00477 nbufsize = cbufsize;
00478
00479 bufsize = nbufsize;
00480
00481 vector<FmtConv*>::iterator it = FmtList.begin();
00482 for (; it != FmtList.end(); ++it)
00483 delete *it;
00484 FmtList.clear();
00485
00486 return FiltChain;
00487 }
00488
00489 VideoFilter * FilterManager::LoadFilter(const FilterInfo *FiltInfo,
00490 VideoFrameType inpixfmt,
00491 VideoFrameType outpixfmt, int &width,
00492 int &height, const char *opts,
00493 int max_threads)
00494 {
00495 void *handle;
00496 VideoFilter *Filter;
00497
00498 if (FiltInfo == NULL)
00499 {
00500 LOG(VB_GENERAL, LOG_ERR, "FilterManager: LoadFilter called with NULL"
00501 "FilterInfo");
00502 return NULL;
00503 }
00504
00505 if (FiltInfo->libname == NULL)
00506 {
00507 LOG(VB_GENERAL, LOG_ERR,
00508 "FilterManager: LoadFilter called with invalid "
00509 "FilterInfo (libname is NULL)");
00510 return NULL;
00511 }
00512
00513 handle = dlopen(FiltInfo->libname, RTLD_NOW);
00514
00515 if (!handle)
00516 {
00517 LOG(VB_GENERAL, LOG_ERR,
00518 QString("FilterManager: unable to load "
00519 "shared library '%1', dlopen reports error '%2'")
00520 .arg(FiltInfo->libname)
00521 .arg(dlerror()));
00522 return NULL;
00523 }
00524
00525 const ConstFilterInfo *filtInfo
00526 = (const ConstFilterInfo*)dlsym(handle, "filter_table");
00527
00528 if (!filtInfo || !filtInfo->filter_init)
00529 {
00530 LOG(VB_GENERAL, LOG_ERR,
00531 QString("FilterManager: unable to load filter "
00532 "'%1' from shared library '%2', dlopen reports error '%3'")
00533 .arg(FiltInfo->name)
00534 .arg(FiltInfo->libname)
00535 .arg(dlerror()));
00536 dlclose(handle);
00537 return NULL;
00538 }
00539
00540 Filter = filtInfo->filter_init(inpixfmt, outpixfmt, &width, &height,
00541 const_cast<char*>(opts), max_threads);
00542
00543 if (Filter == NULL)
00544 {
00545 dlclose(handle);
00546 return NULL;
00547 }
00548
00549 Filter->handle = handle;
00550 Filter->inpixfmt = inpixfmt;
00551 Filter->outpixfmt = outpixfmt;
00552 if (opts)
00553 Filter->opts = strdup(opts);
00554 else
00555 Filter->opts = NULL;
00556 Filter->info = const_cast<FilterInfo*>(FiltInfo);
00557 return Filter;
00558 }