MythTV  master
filtermanager.cpp
Go to the documentation of this file.
1 // POSIX headers
2 #include <cstdlib>
3 
4 #ifndef _WIN32 // dlfcn for mingw defined in compat.h
5 #include <dlfcn.h> // needed for dlopen(), dlerror(), dlsym(), and dlclose()
6 #else
7 #include "compat.h"
8 #endif
9 
10 // Qt headers
11 #include <QDir>
12 #include <QStringList>
13 
14 // MythTV headers
15 #include "mythcontext.h"
16 #include "filtermanager.h"
17 #include "mythdirs.h"
18 
19 #define LOC QString("FilterManager: ")
20 
21 static const char *FmtToString(VideoFrameType ft)
22 {
23  switch(ft)
24  {
25  case FMT_NONE:
26  return "NONE";
27  case FMT_RGB24:
28  return "RGB24";
29  case FMT_YV12:
30  return "YV12";
31  case FMT_ARGB32:
32  return "ARGB32";
33  case FMT_YUV422P:
34  return "YUV422P";
35  default:
36  return "INVALID";
37  }
38 }
39 
41 {
42  for (auto it = m_filters.begin(); it != m_filters.end(); ++it)
43  {
44  VideoFilter *filter = *it;
45  if (filter->opts)
46  free(filter->opts);
47  if (filter->cleanup)
48  filter->cleanup(filter);
49  dlclose(filter->handle);
50  free(filter);
51  }
52  m_filters.clear();
53 }
54 
56 {
57  if (!frame)
58  return;
59 
60  for (auto it = m_filters.begin(); it != m_filters.end(); ++it)
61  (*it)->filter(*it, frame, kScan_Intr2ndField == scan);
62 }
63 
65 {
66  QDir FiltDir(GetFiltersDir());
67 
68  FiltDir.setFilter(QDir::Files | QDir::Readable);
69  QString filter = GetFiltersNameFilter();
70  FiltDir.setNameFilters(QStringList(filter));
71  if (FiltDir.exists())
72  {
73  QStringList LibList = FiltDir.entryList();
74  for (QStringList::iterator i = LibList.begin(); i != LibList.end();
75  ++i)
76  {
77  QString path = FiltDir.filePath(*i);
78  if (path.length() <= 1)
79  continue;
80 
81  LOG(VB_PLAYBACK | VB_FILE, LOG_INFO, LOC +
82  QString("Loading filter '%1'").arg(path));
83 
84  if (!LoadFilterLib(path))
85  {
86  LOG(VB_GENERAL, LOG_WARNING, LOC +
87  QString("Failed to load filter library: %1").arg(path));
88  }
89  }
90  }
91  else
92  LOG(VB_GENERAL, LOG_ERR,
93  "Filter dir '" + FiltDir.absolutePath() + "' doesn't exist?");
94 }
95 
97 {
98  for (auto itf = m_filters.begin(); itf != m_filters.end(); ++itf)
99  {
100  FilterInfo *tmp = itf->second;
101  itf->second = nullptr;
102 
103  free(tmp->name);
104  free(tmp->descript);
105  free(tmp->libname);
106  delete [] (tmp->formats);
107  delete tmp;
108  }
109  m_filters.clear();
110 
111  for (auto ith = m_dlhandles.begin(); ith != m_dlhandles.end(); ++ith)
112  {
113  void *tmp = ith->second;
114  ith->second = nullptr;
115  dlclose(tmp);
116  }
117  m_dlhandles.clear();
118 }
119 
120 bool FilterManager::LoadFilterLib(const QString &path)
121 {
122  dlerror(); // clear out any pre-existing dlerrors
123 
124  void *dlhandle = nullptr;
125  library_map_t::iterator it = m_dlhandles.find(path);
126  if (it != m_dlhandles.end())
127  dlhandle = it->second;
128 
129  if (!dlhandle)
130  {
131  QByteArray apath = path.toLatin1();
132  dlhandle = dlopen(apath.constData(), RTLD_LAZY);
133  if (!dlhandle)
134  {
135  const char *errmsg = dlerror();
136  LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to load filter library: " +
137  QString("'%1'").arg(path) + "\n\t\t\t" + errmsg);
138  return false;
139  }
140  m_dlhandles[path] = dlhandle;
141  }
142 
143  const FilterInfo *filtInfo =
144  (const FilterInfo*) dlsym(dlhandle, "filter_table");
145 
146  if (!filtInfo)
147  {
148  const char *errmsg = dlerror();
149  LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to load filter symbol: " +
150  QString("'%1'").arg(path) + "\n\t\t\t" + errmsg);
151  return false;
152  }
153 
154  for (; filtInfo->filter_init; filtInfo++)
155  {
156  if (!filtInfo->filter_init || !filtInfo->name || !filtInfo->formats)
157  break;
158 
159  FilterInfo *newFilter = new FilterInfo;
160  newFilter->filter_init = nullptr;
161  newFilter->name = strdup(filtInfo->name);
162  newFilter->descript = strdup(filtInfo->descript);
163 
164  int i = 0;
165  for (; filtInfo->formats[i].in != FMT_NONE; i++);
166 
167  newFilter->formats = new FmtConv[i + 1];
168  memcpy(newFilter->formats, filtInfo->formats,
169  sizeof(FmtConv) * (i + 1));
170 
171  QByteArray libname = path.toLatin1();
172  newFilter->libname = strdup(libname.constData());
173  m_filters[newFilter->name] = newFilter;
174  LOG(VB_PLAYBACK, LOG_DEBUG, LOC + QString("filters[%1] = 0x%2")
175  .arg(newFilter->name).arg((uint64_t)newFilter,0,16));
176  }
177  return true;
178 }
179 
180 const FilterInfo *FilterManager::GetFilterInfo(const QString &name) const
181 {
182  const FilterInfo *finfo = nullptr;
183  filter_map_t::const_iterator it = m_filters.find(name);
184  if (it != m_filters.end())
185  finfo = it->second;
186 
187  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("GetFilterInfo(%1)").arg(name) +
188  QString(" returning: 0x%1").arg((uint64_t)finfo,0,16));
189 
190  return finfo;
191 }
192 
193 FilterChain *FilterManager::LoadFilters(const QString& Filters,
194  VideoFrameType &inpixfmt,
195  VideoFrameType &outpixfmt, int &width,
196  int &height, int &bufsize,
197  int max_threads)
198 {
199  if (Filters.toLower() == "none")
200  return nullptr;
201 
202  vector<const FilterInfo*> FiltInfoChain;
203  FilterChain *FiltChain = new FilterChain;
204  vector<FmtConv*> FmtList;
205  const FilterInfo *FI;
206  const FilterInfo *FI2;
207  QString Opts;
208  const FilterInfo *Convert = GetFilterInfo("convert");
209  QStringList OptsList;
210  QStringList FilterList = Filters.split(",", QString::SkipEmptyParts);
211  VideoFilter *NewFilt = nullptr;
212  FmtConv *FC, *FC2, *S1, *S2, *S3;
213  VideoFrameType ifmt;
214  int nbufsize;
215  int cbufsize;
216  int postfilt_width = width;
217  int postfilt_height = height;
218 
219  for (auto i = FilterList.begin(); i != FilterList.end(); ++i)
220  {
221  QString FiltName = (*i).section('=', 0, 0);
222  QString FiltOpts = (*i).section('=', 1);
223 
224  if (FiltName.contains("opengl", Qt::CaseInsensitive) ||
225  FiltName.contains("vdpau", Qt::CaseInsensitive))
226  continue;
227 
228  FI = GetFilterInfo(FiltName);
229 
230  if (FI)
231  {
232  FiltInfoChain.push_back(FI);
233  OptsList.push_back(FiltOpts);
234  }
235  else
236  {
237  LOG(VB_GENERAL, LOG_ERR, LOC +
238  QString("Failed to load filter '%1', "
239  "no such filter exists").arg(FiltName));
240  FiltInfoChain.clear();
241  break;
242  }
243  }
244 
245  ifmt = inpixfmt;
246  for (size_t i = 0; i < FiltInfoChain.size(); i++)
247  {
248  S1 = S2 = S3 = nullptr;
249  FI = FiltInfoChain[i];
250  if (FiltInfoChain.size() - i == 1)
251  {
252  for (FC = FI->formats; FC->in != FMT_NONE; FC++)
253  {
254  if (FC->out == outpixfmt && FC->in == ifmt)
255  {
256  S1 = FC;
257  break;
258  }
259  if (FC->in == ifmt && !S2)
260  S2 = FC;
261  if (FC->out == outpixfmt && !S3)
262  S3 = FC;
263  }
264  }
265  else
266  {
267  FI2 = FiltInfoChain[i+1];
268  for (FC = FI->formats; FC->in != FMT_NONE; FC++)
269  {
270  for (FC2 = FI2->formats; FC2->in != FMT_NONE; FC2++)
271  {
272  if (FC->in == ifmt && FC->out == FC2->in)
273  {
274  S1 = FC;
275  break;
276  }
277  if (FC->out == FC2->in && !S3)
278  S3 = FC;
279  }
280  if (S1)
281  break;
282  if (FC->in == ifmt && !S2)
283  S2 = FC;
284  }
285  }
286 
287  if (S1)
288  FC = S1;
289  else if (S2)
290  FC = S2;
291  else if (S3)
292  FC = S3;
293  else
294  FC = FI->formats;
295 
296  if (FC->in != ifmt && (i > 0 || ifmt != FMT_NONE))
297  {
298  if (!Convert)
299  {
300  LOG(VB_GENERAL, LOG_ERR, "FilterManager: format conversion "
301  "needed but convert filter not found");
302  FiltInfoChain.clear();
303  break;
304  }
305  FiltInfoChain.insert(FiltInfoChain.begin() + i, Convert);
306  OptsList.insert(i, QString ());
307  FmtList.push_back(new FmtConv);
308  if (FmtList.back())
309  {
310  FmtList.back()->in = ifmt;
311  FmtList.back()->out = FC->in;
312  i++;
313  }
314  else
315  {
316  LOG(VB_GENERAL, LOG_ERR,
317  "FilterManager: memory allocation "
318  "failure, returning empty filter chain");
319  FiltInfoChain.clear();
320  break;
321  }
322  }
323  FmtList.push_back(new FmtConv);
324  if (FmtList.back())
325  {
326  FmtList.back()->in = FC->in;
327  FmtList.back()->out = FC->out;
328  }
329  else
330  {
331  LOG(VB_GENERAL, LOG_ERR,
332  "FilterManager: memory allocation failure, "
333  "returning empty filter chain");
334  FiltInfoChain.clear();
335  break;
336  }
337  ifmt = FC->out;
338  }
339 
340  if (ifmt != outpixfmt && outpixfmt != FMT_NONE &&
341  (!FiltInfoChain.empty() || inpixfmt != FMT_NONE))
342  {
343  if (!Convert)
344  {
345  LOG(VB_GENERAL, LOG_ERR, "FilterManager: format conversion "
346  "needed but convert filter not found");
347  FiltInfoChain.clear();
348  }
349  else
350  {
351  FiltInfoChain.push_back(Convert);
352  OptsList.push_back( QString ());
353  FmtList.push_back(new FmtConv);
354  if (FmtList.back())
355  {
356  FmtList.back()->in = ifmt;
357  FmtList.back()->out = outpixfmt;
358  }
359  else
360  {
361  LOG(VB_GENERAL, LOG_ERR,
362  "FilterManager: memory allocation "
363  "failure, returning empty filter chain");
364  FiltInfoChain.clear();
365  }
366  }
367  }
368 
369  nbufsize = -1;
370 
371  if (FiltInfoChain.empty())
372  {
373  delete FiltChain;
374  FiltChain = nullptr;
375  }
376 
377  for (size_t i = 0; i < FiltInfoChain.size(); i++)
378  {
379  QByteArray tmp = OptsList[i].toLocal8Bit();
380  NewFilt = LoadFilter(FiltInfoChain[i], FmtList[i]->in,
381  FmtList[i]->out, postfilt_width,
382  postfilt_height, tmp.constData(),
383  max_threads);
384 
385  if (!NewFilt)
386  {
387  delete FiltChain;
388  LOG(VB_GENERAL, LOG_ERR, QString("FilterManager: failed to load "
389  "filter %1 %2->%3 with args %4")
390  .arg(FiltInfoChain[i]->name)
391  .arg(FmtToString(FmtList[i]->in))
392  .arg(FmtToString(FmtList[i]->out))
393  .arg(OptsList[i]));
394  FiltChain = nullptr;
395  nbufsize = -1;
396  break;
397  }
398 
399  if (NewFilt->filter && FiltChain)
400  {
401  FiltChain->Append(NewFilt);
402  }
403  else
404  {
405  if (NewFilt->opts)
406  free(NewFilt->opts);
407  if (NewFilt->cleanup)
408  NewFilt->cleanup(NewFilt);
409  dlclose(NewFilt->handle);
410  free(NewFilt);
411  }
412 
413  switch (FmtList[i]->out)
414  {
415  case FMT_YV12:
416  cbufsize = postfilt_width * postfilt_height * 3 / 2;
417  break;
418  case FMT_YUV422P:
419  cbufsize = postfilt_width * postfilt_height * 2;
420  break;
421  case FMT_RGB24:
422  cbufsize = postfilt_width * postfilt_height * 3;
423  break;
424  case FMT_ARGB32:
425  cbufsize = postfilt_width * postfilt_height * 4;
426  break;
427  default:
428  cbufsize = 0;
429  }
430 
431  if (cbufsize > nbufsize)
432  nbufsize = cbufsize;
433  }
434 
435  if (FiltChain)
436  {
437  if (inpixfmt == FMT_NONE)
438  inpixfmt = FmtList.front()->in;
439  if (outpixfmt == FMT_NONE)
440  outpixfmt = FmtList.back()->out;
441  width = postfilt_width;
442  height = postfilt_height;
443  }
444  else
445  {
446  if (inpixfmt == FMT_NONE && outpixfmt == FMT_NONE)
447  inpixfmt = outpixfmt = FMT_YV12;
448  else if (inpixfmt == FMT_NONE)
449  inpixfmt = outpixfmt;
450  else if (outpixfmt == FMT_NONE)
451  outpixfmt = inpixfmt;
452  }
453 
454  switch (inpixfmt)
455  {
456  case FMT_YV12:
457  cbufsize = postfilt_width * postfilt_height * 3 / 2;
458  break;
459  case FMT_YUV422P:
460  cbufsize = postfilt_width * postfilt_height * 2;
461  break;
462  case FMT_RGB24:
463  cbufsize = postfilt_width * postfilt_height * 3;
464  break;
465  case FMT_ARGB32:
466  cbufsize = postfilt_width * postfilt_height * 4;
467  break;
468  default:
469  cbufsize = 0;
470  }
471 
472  if (cbufsize > nbufsize)
473  nbufsize = cbufsize;
474 
475  bufsize = nbufsize;
476 
477  for (auto it = FmtList.begin(); it != FmtList.end(); ++it)
478  delete *it;
479  FmtList.clear();
480 
481  return FiltChain;
482 }
483 
485  VideoFrameType inpixfmt,
486  VideoFrameType outpixfmt, int &width,
487  int &height, const char *opts,
488  int max_threads)
489 {
490  void *handle;
491  VideoFilter *Filter;
492 
493  if (FiltInfo == nullptr)
494  {
495  LOG(VB_GENERAL, LOG_ERR, "FilterManager: LoadFilter called with NULL"
496  "FilterInfo");
497  return nullptr;
498  }
499 
500  if (FiltInfo->libname == nullptr)
501  {
502  LOG(VB_GENERAL, LOG_ERR,
503  "FilterManager: LoadFilter called with invalid "
504  "FilterInfo (libname is NULL)");
505  return nullptr;
506  }
507 
508  handle = dlopen(FiltInfo->libname, RTLD_NOW);
509 
510  if (!handle)
511  {
512  LOG(VB_GENERAL, LOG_ERR,
513  QString("FilterManager: unable to load "
514  "shared library '%1', dlopen reports error '%2'")
515  .arg(FiltInfo->libname)
516  .arg(dlerror()));
517  return nullptr;
518  }
519 
520  const FilterInfo *filtInfo =
521  (const FilterInfo*) dlsym(handle, "filter_table");
522 
523  if (!filtInfo || !filtInfo->filter_init)
524  {
525  LOG(VB_GENERAL, LOG_ERR,
526  QString("FilterManager: unable to load filter "
527  "'%1' from shared library '%2', dlopen reports error '%3'")
528  .arg(FiltInfo->name)
529  .arg(FiltInfo->libname)
530  .arg(dlerror()));
531  dlclose(handle);
532  return nullptr;
533  }
534 
535  Filter = filtInfo->filter_init(inpixfmt, outpixfmt, &width, &height,
536  const_cast<char*>(opts), max_threads);
537 
538  if (Filter == nullptr)
539  {
540  dlclose(handle);
541  return nullptr;
542  }
543 
544  Filter->handle = handle;
545  Filter->inpixfmt = inpixfmt;
546  Filter->outpixfmt = outpixfmt;
547  if (opts)
548  Filter->opts = strdup(opts);
549  else
550  Filter->opts = nullptr;
551  Filter->info = const_cast<FilterInfo*>(FiltInfo);
552  return Filter;
553 }
int(* filter)(struct VideoFilter_ *, VideoFrame *, int)
Definition: filter.h:37
void Append(VideoFilter *f)
Definition: filtermanager.h:28
init_filter filter_init
Definition: filter.h:28
struct FilterInfo_ FilterInfo
def scan(profile, smoonURL, gate)
Definition: scan.py:43
#define dlopen(x, y)
Definition: compat.h:233
vector< VideoFilter * > m_filters
Definition: filtermanager.h:31
const char * dlerror(void)
Definition: compat.h:238
void(* cleanup)(struct VideoFilter_ *)
Definition: filter.h:38
FrameScanType
Definition: videoouttypes.h:80
#define dlclose(x)
Definition: compat.h:234
char * libname
Definition: filter.h:32
enum FrameType_ VideoFrameType
void ProcessFrame(VideoFrame *Frame, FrameScanType scan=kScan_Ignore)
FmtConv FmtList[]
static guint32 * tmp
Definition: goom_core.c:35
bool LoadFilterLib(const QString &path)
library_map_t m_dlhandles
Definition: filtermanager.h:55
filter_map_t m_filters
Definition: filtermanager.h:56
FilterChain * LoadFilters(const QString &filters, VideoFrameType &inpixfmt, VideoFrameType &outpixfmt, int &width, int &height, int &bufsize, int max_threads=1)
QString GetFiltersDir(void)
Definition: mythdirs.cpp:228
#define LOC
virtual ~FilterChain()
char * name
Definition: filter.h:29
VideoFilter * LoadFilter(const FilterInfo *Filt, VideoFrameType inpixfmt, VideoFrameType outpixfmt, int &width, int &height, const char *opts, int max_threads)
VideoFrameType inpixfmt
Definition: filter.h:41
const FilterInfo * GetFilterInfo(const QString &name) const
FmtConv * formats
Definition: filter.h:31
char * descript
Definition: filter.h:30
static const char * FmtToString(VideoFrameType ft)
const char * name
Definition: ParseText.cpp:328
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
QString GetFiltersNameFilter(void)
Definition: mythdirs.cpp:283
VideoFrameType in
Definition: filter.h:16
#define RTLD_LAZY
Definition: compat.h:232
VideoFrameType outpixfmt
Definition: filter.h:42
FilterInfo * info
Definition: filter.h:44
VideoFrameType out
Definition: filter.h:17
char * opts
Definition: filter.h:43
#define dlsym(x, y)
Definition: compat.h:235
void * handle
Definition: filter.h:40