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.h>
00012 #include <qstringlist.h>
00013 #include <qstrlist.h>
00014
00015
00016 #include "filtermanager.h"
00017 #include "mythcontext.h"
00018
00019 static const char *FmtToString(VideoFrameType ft)
00020 {
00021 switch(ft)
00022 {
00023 case FMT_NONE:
00024 return "NONE";
00025 case FMT_RGB24:
00026 return "RGB24";
00027 case FMT_YV12:
00028 return "YV12";
00029 case FMT_ARGB32:
00030 return "ARGB32";
00031 case FMT_YUV422P:
00032 return "YUV422P";
00033 default:
00034 return "INVALID";
00035 }
00036 }
00037
00038 FilterChain::FilterChain(void)
00039 {
00040 setAutoDelete(true);
00041 }
00042
00043 FilterChain::~FilterChain()
00044 {
00045 clear();
00046 }
00047
00048 void FilterChain::ProcessFrame(VideoFrame *Frame)
00049 {
00050 if (!Frame)
00051 return;
00052
00053 VideoFilter *VF = first();
00054 while (VF)
00055 {
00056 VF->filter(VF, Frame);
00057 VF = next();
00058 }
00059 }
00060
00061 void FilterChain::deleteItem(QPtrCollection::Item d)
00062 {
00063 if (del_item)
00064 {
00065 VideoFilter *Filter = (VideoFilter *)d;
00066 if (Filter->opts)
00067 free(Filter->opts);
00068 if (Filter->cleanup)
00069 Filter->cleanup(Filter);
00070 dlclose(Filter->handle);
00071 free(Filter);
00072 }
00073 }
00074
00075 FilterManager::FilterManager()
00076 {
00077 QDir FiltDir(gContext->GetFiltersDir());
00078 QString Path;
00079
00080 FiltDir.setFilter(QDir::Files | QDir::Readable);
00081 if (FiltDir.exists())
00082 {
00083 QStringList LibList = FiltDir.entryList();
00084 for (QStringList::iterator i = LibList.begin(); i != LibList.end();
00085 i++)
00086 {
00087 Path = FiltDir.filePath(*i);
00088 if (Path.length() > 1)
00089 LoadFilterLib(Path);
00090 }
00091 }
00092 else
00093 VERBOSE(VB_IMPORTANT,
00094 "Filter dir '" + FiltDir.absPath() + "' doesn't exist?");
00095 }
00096
00097 FilterManager::~FilterManager()
00098 {
00099 for (QPtrListIterator<FilterInfo> i(Filters); i.current (); ++i)
00100 {
00101 free(i.current()->symbol);
00102 free(i.current()->name);
00103 free(i.current()->descript);
00104 free(i.current()->libname);
00105 delete [] (i.current()->formats);
00106 delete i.current();
00107 }
00108 }
00109
00110 void FilterManager::LoadFilterLib(QString Path)
00111 {
00112 void *handle;
00113 int i;
00114 FilterInfo *FiltInfo;
00115
00116 handle = dlopen(Path.ascii(), RTLD_LAZY);
00117 if (handle)
00118 {
00119 FiltInfo = (FilterInfo *)dlsym(handle, "filter_table");
00120 if (FiltInfo)
00121 {
00122 for (; FiltInfo->symbol != NULL; FiltInfo++)
00123 {
00124 if (FiltInfo->symbol == NULL || FiltInfo->name == NULL
00125 || FiltInfo->formats == NULL)
00126 break;
00127
00128 FilterInfo *NewFilt = new FilterInfo;
00129 NewFilt->symbol = strdup(FiltInfo->symbol);
00130 NewFilt->name = strdup(FiltInfo->name);
00131 NewFilt->descript = strdup(FiltInfo->descript);
00132
00133 for (i = 0; FiltInfo->formats[i].in != FMT_NONE; i++)
00134 ;
00135
00136 NewFilt->formats = new FmtConv[i + 1];
00137 memcpy(NewFilt->formats, FiltInfo->formats,
00138 sizeof(FmtConv) * (i + 1));
00139
00140 NewFilt->libname = strdup(Path.ascii());
00141 FilterByName.insert(NewFilt->name, NewFilt);
00142 Filters.append(NewFilt);
00143 }
00144 }
00145 }
00146 }
00147
00148 FilterChain *FilterManager::LoadFilters(QString Filters,
00149 VideoFrameType &inpixfmt,
00150 VideoFrameType &outpixfmt, int &width,
00151 int &height, int &bufsize)
00152 {
00153 QPtrList<FilterInfo> FiltInfoChain;
00154 FilterChain *FiltChain = new FilterChain;
00155 QPtrList<FmtConv> FmtList;
00156 FilterInfo *FI, *FI2;
00157 QString Opts;
00158 FilterInfo *Convert = GetFilterInfoByName("convert");
00159 QStrList OptsList (TRUE);
00160 QStringList FilterList = QStringList::split(",", Filters);
00161 VideoFilter *NewFilt = NULL;
00162 FmtConv *FC, *FC2, *S1, *S2, *S3;
00163 VideoFrameType ifmt;
00164 unsigned int i;
00165 int nbufsize;
00166 int cbufsize;
00167 int postfilt_width = width;
00168 int postfilt_height = height;
00169 FmtList.setAutoDelete(TRUE);
00170 OptsList.setAutoDelete(TRUE);
00171
00172 for (QStringList::Iterator i = FilterList.begin();
00173 i != FilterList.end(); ++i)
00174 {
00175 QString FiltName = (*i).section('=', 0, 0);
00176 QString FiltOpts = (*i).section('=', 1);
00177 FI = GetFilterInfoByName(FiltName);
00178
00179 if (FI)
00180 {
00181 FiltInfoChain.append(FI);
00182 OptsList.append(FiltOpts);
00183 }
00184 else
00185 {
00186 VERBOSE(VB_IMPORTANT,QString("FilterManager: failed to load "
00187 "filter '%1', no such filter exists").arg(FiltName));
00188 FiltInfoChain.clear();
00189 break;
00190 }
00191 }
00192
00193 ifmt = inpixfmt;
00194 for (i = 0; i < FiltInfoChain.count(); i++)
00195 {
00196 S1 = S2 = S3 = NULL;
00197 FI = FiltInfoChain.at(i);
00198 if (FiltInfoChain.count() - i == 1)
00199 {
00200 for (FC = FI->formats; FC->in != FMT_NONE; FC++)
00201 {
00202 if (FC->out == outpixfmt && FC->in == ifmt)
00203 {
00204 S1 = FC;
00205 break;
00206 }
00207 if (FC->in == ifmt && !S2)
00208 S2 = FC;
00209 if (FC->out == outpixfmt && !S3)
00210 S3 = FC;
00211 }
00212 }
00213 else
00214 {
00215 FI2 = FiltInfoChain.at(i+1);
00216 for (FC = FI->formats; FC->in != FMT_NONE; FC++)
00217 {
00218 for (FC2 = FI2->formats; FC2->in != FMT_NONE; FC2++)
00219 {
00220 if (FC->in == ifmt && FC->out == FC2->in)
00221 {
00222 S1 = FC;
00223 break;
00224 }
00225 if (FC->out == FC2->in && !S3)
00226 S3 = FC;
00227 }
00228 if (S1)
00229 break;
00230 if (FC->in == ifmt && !S2)
00231 S2 = FC;
00232 }
00233 }
00234
00235 if (S1)
00236 FC = S1;
00237 else if (S2)
00238 FC = S2;
00239 else if (S3)
00240 FC = S3;
00241 else
00242 FC = FI->formats;
00243
00244 if (FC->in != ifmt && (i > 0 || ifmt != FMT_NONE))
00245 {
00246 if (!Convert)
00247 {
00248 VERBOSE(VB_IMPORTANT, "FilterManager: format conversion "
00249 "needed but convert filter not found");
00250 FiltInfoChain.clear();
00251 break;
00252 }
00253 FiltInfoChain.insert(i, Convert);
00254 OptsList.insert(i, QString ());
00255 FmtList.append(new FmtConv);
00256 if (FmtList.last())
00257 {
00258 FmtList.last()->in = ifmt;
00259 FmtList.last()->out = FC->in;
00260 i++;
00261 }
00262 else
00263 {
00264 VERBOSE(VB_IMPORTANT, "FilterManager: memory allocation "
00265 "failure, returning empty filter chain");
00266 FiltInfoChain.clear();
00267 break;
00268 }
00269 }
00270 FmtList.append(new FmtConv);
00271 if (FmtList.last())
00272 {
00273 FmtList.last()->in = FC->in;
00274 FmtList.last()->out = FC->out;
00275 }
00276 else
00277 {
00278 VERBOSE(VB_IMPORTANT, "FilterManager: memory allocation failure, "
00279 "returning empty filter chain");
00280 FiltInfoChain.clear();
00281 break;
00282 }
00283 ifmt = FC->out;
00284 }
00285
00286 if (ifmt != outpixfmt && outpixfmt != FMT_NONE &&
00287 (FiltInfoChain.count() || inpixfmt != FMT_NONE))
00288 {
00289 if (!Convert)
00290 {
00291 VERBOSE(VB_IMPORTANT, "FilterManager: format conversion "
00292 "needed but convert filter not found");
00293 FiltInfoChain.clear();
00294 }
00295 else
00296 {
00297 FiltInfoChain.append(Convert);
00298 OptsList.append( QString ());
00299 FmtList.append(new FmtConv);
00300 if (FmtList.last())
00301 {
00302 FmtList.last()->in = ifmt;
00303 FmtList.last()->out = outpixfmt;
00304 }
00305 else
00306 {
00307 VERBOSE(VB_IMPORTANT, "FilterManager: memory allocation "
00308 "failure, returning empty filter chain");
00309 FiltInfoChain.clear();
00310 }
00311 }
00312 }
00313
00314 nbufsize = -1;
00315
00316 if (!FiltInfoChain.count())
00317 {
00318 delete FiltChain;
00319 FiltChain = NULL;
00320 }
00321
00322 for (i = 0; i < FiltInfoChain.count(); i++)
00323 {
00324 NewFilt = LoadFilter(FiltInfoChain.at(i), FmtList.at(i)->in,
00325 FmtList.at(i)->out, postfilt_width,
00326 postfilt_height, OptsList.at(i));
00327
00328 if (!NewFilt)
00329 {
00330 delete FiltChain;
00331 VERBOSE(VB_IMPORTANT,QString("FilterManager: failed to load "
00332 "filter %1 %2->%3 with args %4")
00333 .arg(FiltInfoChain.at(i)->name)
00334 .arg(FmtToString(FmtList.at(i)->in))
00335 .arg(FmtToString(FmtList.at(i)->out))
00336 .arg(OptsList.at(i)?OptsList.at(i):"NULL")
00337 );
00338 FiltChain = NULL;
00339 nbufsize = -1;
00340 break;
00341 }
00342
00343 if (NewFilt->filter)
00344 FiltChain->append(NewFilt);
00345 else
00346 {
00347 if (NewFilt->opts)
00348 free(NewFilt->opts);
00349 if (NewFilt->cleanup)
00350 NewFilt->cleanup(NewFilt);
00351 dlclose(NewFilt->handle);
00352 free(NewFilt);
00353 }
00354
00355 switch (FmtList.at(i)->out)
00356 {
00357 case FMT_YV12:
00358 cbufsize = postfilt_width * postfilt_height * 3 / 2;
00359 break;
00360 case FMT_YUV422P:
00361 cbufsize = postfilt_width * postfilt_height * 2;
00362 break;
00363 case FMT_RGB24:
00364 cbufsize = postfilt_width * postfilt_height * 3;
00365 break;
00366 case FMT_ARGB32:
00367 cbufsize = postfilt_width * postfilt_height * 4;
00368 break;
00369 default:
00370 cbufsize = 0;
00371 }
00372
00373 if (cbufsize > nbufsize)
00374 nbufsize = cbufsize;
00375 }
00376
00377 if (FiltChain)
00378 {
00379 if (inpixfmt == FMT_NONE)
00380 inpixfmt = FmtList.first()->in;
00381 if (outpixfmt == FMT_NONE)
00382 inpixfmt = FmtList.last()->out;
00383 width = postfilt_width;
00384 height = postfilt_height;
00385 }
00386 else
00387 {
00388 if (inpixfmt == FMT_NONE && outpixfmt == FMT_NONE)
00389 inpixfmt = outpixfmt = FMT_YV12;
00390 else if (inpixfmt == FMT_NONE)
00391 inpixfmt = outpixfmt;
00392 else if (outpixfmt == FMT_NONE)
00393 outpixfmt = inpixfmt;
00394 }
00395
00396 switch (inpixfmt)
00397 {
00398 case FMT_YV12:
00399 cbufsize = postfilt_width * postfilt_height * 3 / 2;
00400 break;
00401 case FMT_YUV422P:
00402 cbufsize = postfilt_width * postfilt_height * 2;
00403 break;
00404 case FMT_RGB24:
00405 cbufsize = postfilt_width * postfilt_height * 3;
00406 break;
00407 case FMT_ARGB32:
00408 cbufsize = postfilt_width * postfilt_height * 4;
00409 break;
00410 default:
00411 cbufsize = 0;
00412 }
00413
00414 if (cbufsize > nbufsize)
00415 nbufsize = cbufsize;
00416
00417 bufsize = nbufsize;
00418
00419 return FiltChain;
00420 }
00421
00422 VideoFilter * FilterManager::LoadFilter(FilterInfo *FiltInfo,
00423 VideoFrameType inpixfmt,
00424 VideoFrameType outpixfmt, int &width,
00425 int &height, char *opts)
00426 {
00427 void *handle;
00428 VideoFilter *Filter;
00429 VideoFilter *(*InitFilter)(int, int, int *, int *, char *);
00430
00431 if (FiltInfo == NULL)
00432 {
00433 VERBOSE(VB_IMPORTANT, "FilterManager: LoadFilter called with NULL"
00434 "FilterInfo");
00435 return NULL;
00436 }
00437
00438 if (FiltInfo->libname == NULL)
00439 {
00440 VERBOSE(VB_IMPORTANT, "FilterManager: LoadFilter called with invalid "
00441 "FilterInfo (libname is NULL)");
00442 return NULL;
00443 }
00444
00445 if (FiltInfo->symbol == NULL)
00446 {
00447 VERBOSE(VB_IMPORTANT, "FilterManager: LoadFilter called with invalid "
00448 "FilterInfo (symbol is NULL)");
00449 return NULL;
00450 }
00451
00452 handle = dlopen(FiltInfo->libname, RTLD_NOW);
00453
00454 if (!handle)
00455 {
00456 VERBOSE(VB_IMPORTANT, QString("FilterManager: unable to load "
00457 "shared library '%1', dlopen reports error '%2'")
00458 .arg(FiltInfo->libname)
00459 .arg(dlerror()));
00460 return NULL;
00461 }
00462
00463 InitFilter =
00464 (VideoFilter * (*)(int, int, int *, int *, char *))dlsym(handle,
00465 FiltInfo->
00466 symbol);
00467
00468 if (!InitFilter)
00469 {
00470 VERBOSE(VB_IMPORTANT, QString("FilterManager: unable to load symbol "
00471 "'%1' from shared library '%2', dlopen reports error '%3'")
00472 .arg(FiltInfo->symbol)
00473 .arg(FiltInfo->libname)
00474 .arg(dlerror()));
00475 dlclose(handle);
00476 return NULL;
00477 }
00478
00479 Filter = (*InitFilter)(inpixfmt, outpixfmt, &width, &height, opts);
00480
00481 if (Filter == NULL)
00482 {
00483 dlclose(handle);
00484 return NULL;
00485 }
00486
00487 Filter->handle = handle;
00488 Filter->inpixfmt = inpixfmt;
00489 Filter->outpixfmt = outpixfmt;
00490 if (opts)
00491 Filter->opts = strdup(opts);
00492 else
00493 Filter->opts = NULL;
00494 Filter->info = FiltInfo;
00495 return Filter;
00496 }