MythTV  0.26-pre
filtermanager.cpp
Go to the documentation of this file.
00001 // POSIX headers
00002 #include <stdlib.h>
00003 
00004 #ifndef USING_MINGW // dlfcn for mingw defined in compat.h
00005 #include <dlfcn.h> // needed for dlopen(), dlerror(), dlsym(), and dlclose()
00006 #else
00007 #include "compat.h"
00008 #endif
00009 
00010 // Qt headers
00011 #include <QDir>
00012 #include <QStringList>
00013 
00014 // MythTV headers
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(); // clear out any pre-existing dlerrors
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 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends