Credit Andrew R for finding the direct copy mods for exr and ppm sequences
[goodguy/cinelerra.git] / cinelerra-5.1 / cinelerra / pluginfclient.C
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <stdint.h>
4 #include <unistd.h>
5 #include <string.h>
6 #include <ctype.h>
7
8 #include "bcwindowbase.h"
9 #include "bctitle.h"
10 #include "cstrdup.h"
11 #include "keys.h"
12 #include "language.h"
13 #include "mwindow.h"
14 #include "pluginfclient.h"
15 #include "pluginserver.h"
16 #include "samples.h"
17 #include "vframe.h"
18 #include "filexml.h"
19
20 #ifdef FFMPEG3
21 #define av_filter_iterate(p) ((*(const AVFilter**)(p))=avfilter_next(*(const AVFilter **)(p)))
22 #endif
23
24 static void ff_err(int ret, const char *fmt, ...)
25 {
26         char msg[BCTEXTLEN];
27         va_list ap;
28         va_start(ap, fmt);
29         vsnprintf(msg, sizeof(msg), fmt, ap);
30         va_end(ap);
31         char errmsg[BCSTRLEN];
32         av_strerror(ret, errmsg, sizeof(errmsg));
33         fprintf(stderr,_("%s  err: %s\n"),msg, errmsg);
34 }
35
36 PluginFClientConfig::PluginFClientConfig()
37 {
38         ffilt = 0;
39 }
40
41 PluginFClientConfig::~PluginFClientConfig()
42 {
43         delete ffilt;
44         remove_all_objects();
45 }
46
47 int PluginFClientConfig::equivalent(PluginFClientConfig &that)
48 {
49         PluginFClientConfig &conf = *this;
50         for( int i=0; i<that.size(); ++i ) {
51                 PluginFClient_Opt *topt = conf[i], *vopt = that[i];
52                 char tval[BCTEXTLEN], *tp = topt->get(tval, sizeof(tval));
53                 char vval[BCTEXTLEN], *vp = vopt->get(vval, sizeof(vval));
54                 int ret = tp && vp ? strcmp(tp, vp) : tp || vp ? 1 : 0;
55                 if( ret ) return 0;
56         }
57         return 1;
58 }
59
60 void PluginFClientConfig::copy_from(PluginFClientConfig &that)
61 {
62         PluginFClientConfig &conf = *this;
63         for( int i=0; i<that.size(); ++i ) {
64                 PluginFClient_Opt *vp = that[i];
65                 const char *nm = vp->opt->name;
66                 int k = size();
67                 while( --k >= 0 && strcmp(nm, conf[k]->opt->name) );
68                 if( k < 0 ) continue;
69                 PluginFClient_Opt *fopt = conf[k];
70                 char str[BCTEXTLEN], *sp = vp->get(str, sizeof(str));
71                 if( sp ) fopt->set(sp);
72         }
73 }
74
75 void PluginFClientConfig::interpolate(PluginFClientConfig &prev, PluginFClientConfig &next,
76         int64_t prev_frame, int64_t next_frame, int64_t current_frame)
77 {
78         copy_from(prev);
79 }
80
81 void PluginFClientConfig::initialize(const char *name)
82 {
83         delete ffilt;
84         ffilt = PluginFFilter::new_ffilter(name);
85         const AVOption *opt = 0;
86         void *obj = ffilt->filter_config();
87
88         const AVClass *filt_class = ffilt->filter_class();
89         if( filt_class && filt_class->option ) {
90                 PluginFClientConfig &conf = *this;
91                 while( (opt=av_opt_next(obj, opt)) != 0 ) {
92                         if( opt->type == AV_OPT_TYPE_CONST ) continue;
93                         int dupl = 0;
94                         for( int i=0; !dupl && i<size(); ++i ) {
95                                 PluginFClient_Opt *fp = conf[i];
96                                 const AVOption *op = fp->opt;
97                                 if( op->offset != opt->offset ) continue;
98                                 if( op->type != opt->type ) continue;
99                                 dupl = 1;
100                                 if( strlen(op->name) < strlen(opt->name) )
101                                         fp->opt = opt;
102                         }
103                         if( dupl ) continue;
104                         PluginFClient_Opt *fopt = new PluginFClient_Opt(this, opt);
105                         append(fopt);
106                 }
107         }
108         if( (ffilt->filter->flags & AVFILTER_FLAG_SLICE_THREADS) != 0 ) {
109                 opt = av_opt_find(ffilt->fctx, "threads", NULL, 0, 0);
110                 if( opt ) {
111                         PluginFClient_Opt *fopt = new PluginFClient_Opt(this, opt);
112                         append(fopt);
113                 }
114         }
115 }
116
117 int PluginFClientConfig::update()
118 {
119         int ret = 0;
120         PluginFClientConfig &conf = *this;
121
122         for( int i=0; i<size(); ++i ) {
123                 PluginFClient_Opt *fopt = conf[i];
124                 char val[BCTEXTLEN], *vp = fopt->get(val, sizeof(val));
125                 if( !vp || !strcmp(val, fopt->item_value->get_text()) ) continue;
126                 fopt->item_value->update();
127                 ++ret;
128         }
129         return ret;
130 }
131
132 void PluginFClientConfig::dump(FILE *fp)
133 {
134         const AVOption *opt = 0;
135         const AVClass *obj = filter_class();
136         PluginFClientConfig &conf = *this;
137
138         while( (opt=av_opt_next(&obj, opt)) != 0 ) {
139                 if( opt->type == AV_OPT_TYPE_CONST ) continue;
140                 int k = size();
141                 while( --k >= 0 && strcmp(opt->name, conf[k]->opt->name) );
142                 if( k < 0 ) continue;
143                 PluginFClient_Opt *fopt = conf[k];
144                 char val[BCTEXTLEN], *vp = fopt->get(val,sizeof(val));
145                 fprintf(fp, "  %s:=%s", opt->name, vp);
146                 if( opt->unit ) {
147                         char unt[BCTEXTLEN], *up = unt;
148                         fopt->units(up);
149                         fprintf(fp, "%s", unt);
150                 }
151                 fprintf(fp, "\n");
152         }
153 }
154
155 PluginFClientReset::
156 PluginFClientReset(PluginFClientWindow *fwin, int x, int y)
157  : BC_GenericButton(x, y, _("Reset"))
158 {
159         this->fwin = fwin;
160 }
161
162 PluginFClientReset::
163 ~PluginFClientReset()
164 {
165 }
166
167 int PluginFClientReset::handle_event()
168 {
169         fwin->ffmpeg->config.initialize(fwin->ffmpeg->name);
170         if( fwin->ffmpeg->config.update() > 0 )
171                 fwin->draw();
172         fwin->ffmpeg->plugin->send_configure_change();
173         return 1;
174 }
175
176 PluginFClientText::
177 PluginFClientText(PluginFClientWindow *fwin, int x, int y, int w)
178  : BC_TextBox(x, y, w, 1, (char *)"")
179 {
180         this->fwin = fwin;
181 }
182
183 PluginFClientText::
184 ~PluginFClientText()
185 {
186 }
187
188 int PluginFClientText::handle_event()
189 {
190         if( get_keypress() == RETURN ) {
191                 fwin->update();
192                 activate();
193         }
194         return 0;
195 }
196
197 PluginFClientUnits::
198 PluginFClientUnits(PluginFClientWindow *fwin, int x, int y, int w)
199  : BC_PopupMenu(x, y, w, "")
200 {
201         this->fwin = fwin;
202 }
203
204 PluginFClientUnits::
205 ~PluginFClientUnits()
206 {
207 }
208
209 int PluginFClientUnits::handle_event()
210 {
211         const char *text = get_text();
212         if( text && fwin->selected ) {
213                 if( text ) fwin->selected->set(text);
214                 fwin->selected->item_value->update();
215                 fwin->draw();
216                 fwin->ffmpeg->plugin->send_configure_change();
217         }
218         return 1;
219 }
220
221 PluginFClientApply::
222 PluginFClientApply(PluginFClientWindow *fwin, int x, int y)
223  : BC_GenericButton(x, y, _("Apply"))
224 {
225         this->fwin = fwin;
226 }
227
228 PluginFClientApply::
229 ~PluginFClientApply()
230 {
231 }
232
233 int PluginFClientApply::handle_event()
234 {
235         return fwin->update();
236 }
237
238 PluginFClientPot::PluginFClientPot(PluginFClientWindow *fwin, int x, int y)
239  : BC_FPot(x, y, 0.f, 0.f, 0.f)
240 {
241         this->fwin = fwin;
242 }
243
244 int PluginFClient_Opt::get_range(float &fmn, float &fmx)
245 {
246         switch( opt->type ) {
247         case AV_OPT_TYPE_INT:
248         case AV_OPT_TYPE_INT64:
249         case AV_OPT_TYPE_DOUBLE:
250         case AV_OPT_TYPE_FLOAT: break;
251         default: return 1;
252         }
253         const AVClass *filt_class = filter_class();
254         if( !filt_class || !filt_class->option ) return 1;
255         AVOptionRanges *r = 0;
256         void *obj = &filt_class;
257         if( av_opt_query_ranges(&r, obj, opt->name, AV_OPT_SEARCH_FAKE_OBJ) < 0 ) return 1;
258         int ret = 1;
259         if( r->nb_ranges == 1 ) {
260                 fmn = r->range[0]->value_min;
261                 fmx = r->range[0]->value_max;
262                 ret = 0;
263         }
264         av_opt_freep_ranges(&r);
265         return ret;
266 }
267
268 float PluginFClient_Opt::get_float()
269 {
270         char val[BCTEXTLEN];  val[0] = 0;
271         get(val, sizeof(val));
272         return atof(val);
273 }
274
275 void PluginFClient_Opt::set_float(float v)
276 {
277         char val[BCTEXTLEN];  val[0] = 0;
278         sprintf(val, "%f", v);
279         set(val);
280 }
281
282 int PluginFClientPot::handle_event()
283 {
284         if( fwin->selected ) {
285                 fwin->selected->set_float(get_value());
286                 fwin->update_selected();
287                 return fwin->update();
288         }
289         return 1;
290 }
291
292 PluginFClientSlider::PluginFClientSlider(PluginFClientWindow *fwin, int x, int y)
293  : BC_FSlider(x, y, 0, fwin->get_w()-x-xS(20), fwin->get_w()-x-xS(20), 0.f, 0.f, 0.f)
294 {
295         this->fwin = fwin;
296 }
297
298 int PluginFClientSlider::handle_event()
299 {
300         if( fwin->selected ) {
301                 fwin->selected->set_float(get_value());
302                 fwin->update_selected();
303                 return fwin->update();
304         }
305         return 1;
306 }
307
308
309 PluginFClient_OptPanel::
310 PluginFClient_OptPanel(PluginFClientWindow *fwin, int x, int y, int w, int h)
311  : BC_ListBox(x, y, w, h, LISTBOX_TEXT), opts(items[0]), vals(items[1])
312 {
313         this->fwin = fwin;
314         update();  // init col/wid/columns
315 }
316
317 PluginFClient_OptPanel::
318 ~PluginFClient_OptPanel()
319 {
320 }
321
322 void PluginFClient_OptPanel::create_objects()
323 {
324         PluginFClientConfig &fconfig = fwin->ffmpeg->config;
325         for( int i=0; i<fconfig.size(); ++i ) {
326                 PluginFClient_Opt *opt = fconfig[i];
327                 opts.append(opt->item_name);
328                 vals.append(opt->item_value);
329         }
330 }
331
332 int PluginFClient_OptPanel::cursor_leave_event()
333 {
334         hide_tooltip();
335         return 0;
336 }
337
338 void PluginFClientWindow::update(PluginFClient_Opt *opt)
339 {
340         if( selected != opt ) {
341                 if( selected ) selected->item_name->set_selected(0);
342                 selected = opt;
343                 if( selected ) selected->item_name->set_selected(1);
344         }
345         clear_box(0,0, 0,panel->get_y());
346         char str[BCTEXTLEN], *sp;
347         *(sp=str) = 0;
348         if( opt ) opt->types(sp);
349         type->update(str);
350         *(sp=str) = 0;
351         if( opt ) opt->ranges(sp);
352         range->update(str);
353         while( units->total_items() ) units->del_item(0);
354         ArrayList<const AVOption *> opts;
355         int n = !opt ? 0 : opt->units(opts);
356         for( int i=0; i<n; ++i )
357                 units->add_item(new BC_MenuItem(opts[i]->name, 0));
358         char unit[BCSTRLEN];  strcpy(unit, "()");
359         if( units->total_items() && opt && opt->opt->unit )
360                 strcpy(unit, opt->opt->unit);
361         units->set_text(unit);
362         char val[BCTEXTLEN];  val[0] = 0;
363         if( opt ) opt->get(val, sizeof(val));
364         text->update(val);
365
366         float v = 0, fmn = 0, fmx = 0;
367         if( opt && !opt->get_range(fmn, fmx) ) v = atof(val);
368         float p = (fmx-fmn) / slider->get_w();
369         slider->set_precision(p);
370         slider->update(slider->get_w(), v, fmn, fmx);
371         pot->set_precision(p);
372         pot->update(v, fmn, fmx);
373         panel->update();
374 }
375
376 int PluginFClientWindow::update()
377 {
378         const char *txt = text->get_text();
379         if( txt && selected ) {
380                 selected->set(txt);
381                 selected->item_value->update();
382                 draw();
383                 ffmpeg->plugin->send_configure_change();
384         }
385         return 1;
386 }
387
388 void PluginFClientWindow::update_selected()
389 {
390         update(selected);
391 }
392
393 int PluginFClient_OptPanel::selection_changed()
394 {
395         PluginFClient_Opt *opt = 0;
396         BC_ListBoxItem *item = get_selection(0, 0);
397         if( item ) {
398                 PluginFClient_OptName *opt_name = (PluginFClient_OptName *)item;
399                 opt = opt_name->opt;
400         }
401         fwin->update(opt);
402         fwin->panel->set_tooltip(!opt ? 0 : opt->tip());
403         fwin->panel->show_tooltip();
404         return 1;
405 }
406
407 void *PluginFClient_Opt::filter_config()
408 {
409         return conf->filter_config();
410 }
411 const AVClass *PluginFClient_Opt::filter_class()
412 {
413         return conf->filter_class();
414 }
415
416 int PluginFClient_Opt::types(char *rp)
417 {
418         const char *cp;
419         switch (opt->type) {
420         case AV_OPT_TYPE_FLAGS: cp = "<flags>";  break;
421         case AV_OPT_TYPE_INT: cp = "<int>"; break;
422         case AV_OPT_TYPE_INT64: cp = "<int64>"; break;
423         case AV_OPT_TYPE_DOUBLE: cp = "<double>"; break;
424         case AV_OPT_TYPE_FLOAT: cp = "<float>"; break;
425         case AV_OPT_TYPE_STRING: cp = "<string>"; break;
426         case AV_OPT_TYPE_RATIONAL: cp = "<rational>"; break;
427         case AV_OPT_TYPE_BINARY: cp = "<binary>"; break;
428         case AV_OPT_TYPE_IMAGE_SIZE: cp = "<image_size>"; break;
429         case AV_OPT_TYPE_VIDEO_RATE: cp = "<video_rate>"; break;
430         case AV_OPT_TYPE_PIXEL_FMT: cp = "<pix_fmt>"; break;
431         case AV_OPT_TYPE_SAMPLE_FMT: cp = "<sample_fmt>"; break;
432         case AV_OPT_TYPE_DURATION: cp = "<duration>"; break;
433         case AV_OPT_TYPE_COLOR: cp = "<color>"; break;
434         case AV_OPT_TYPE_CHANNEL_LAYOUT: cp = "<channel_layout>";  break;
435         default: cp = "<undef>";  break;
436         }
437         return sprintf(rp, "%s", cp);
438 }
439 int PluginFClient_Opt::scalar(double d, char *rp)
440 {
441         const char *cp = 0;
442              if( d == INT_MAX ) cp = "INT_MAX";
443         else if( d == INT_MIN ) cp = "INT_MIN";
444         else if( d == UINT32_MAX ) cp = "UINT32_MAX";
445         else if( d == (double)INT64_MAX ) cp = "I64_MAX";
446         else if( d == INT64_MIN ) cp = "I64_MIN";
447         else if( d == FLT_MAX ) cp = "FLT_MAX";
448         else if( d == FLT_MIN ) cp = "FLT_MIN";
449         else if( d == -FLT_MAX ) cp = "-FLT_MAX";
450         else if( d == -FLT_MIN ) cp = "-FLT_MIN";
451         else if( d == DBL_MAX ) cp = "DBL_MAX";
452         else if( d == DBL_MIN ) cp = "DBL_MIN";
453         else if( d == -DBL_MAX ) cp = "-DBL_MAX";
454         else if( d == -DBL_MIN ) cp = "-DBL_MIN";
455         else if( d == 0 ) cp = signbit(d) ? "-0" : "0";
456         else if( isnan(d) ) cp = signbit(d) ? "-NAN" : "NAN";
457         else if( isinf(d) ) cp = signbit(d) ? "-INF" : "INF";
458         else return sprintf(rp, "%g", d);
459         return sprintf(rp, "%s", cp);
460 }
461
462 int PluginFClient_Opt::ranges(char *rp)
463 {
464         const AVClass *filt_class = filter_class();
465         if( !filt_class || !filt_class->option ) return 0;
466         switch (opt->type) {
467         case AV_OPT_TYPE_INT:
468         case AV_OPT_TYPE_INT64:
469         case AV_OPT_TYPE_DOUBLE:
470         case AV_OPT_TYPE_FLOAT: break;
471         default: return 0;;
472         }
473         AVOptionRanges *r = 0;
474         void *obj = &filt_class;
475         char *cp = rp;
476         if( av_opt_query_ranges(&r, obj, opt->name, AV_OPT_SEARCH_FAKE_OBJ) < 0 ) return 0;
477         for( int i=0; i<r->nb_ranges; ++i ) {
478                 cp += sprintf(cp, " (");  cp += scalar(r->range[i]->value_min, cp);
479                 cp += sprintf(cp, "..");  cp += scalar(r->range[i]->value_max, cp);
480                 cp += sprintf(cp, ")");
481         }
482         av_opt_freep_ranges(&r);
483         return cp - rp;
484 }
485
486 int PluginFClient_Opt::units(ArrayList<const AVOption *> &opts)
487 {
488         if( !this->opt->unit ) return 0;
489         const AVClass *filt_class = filter_class();
490         if( !filt_class || !filt_class->option ) return 0;
491         void *obj = &filt_class;
492         const AVOption *opt = NULL;
493         while( (opt=av_opt_next(obj, opt)) != 0 ) {
494                 if( !opt->unit ) continue;
495                 if( opt->type != AV_OPT_TYPE_CONST ) continue;
496                 if( strcmp(this->opt->unit, opt->unit) ) continue;
497                 int i = opts.size();
498                 while( --i >= 0 ) {
499                         if( opts[i]->default_val.i64 != opt->default_val.i64 ) continue;
500                         if( strlen(opts[i]->name) < strlen(opt->name) ) opts[i] = opt;
501                         break;
502                 }
503                 if( i < 0 )
504                         opts.append(opt);
505         }
506         return opts.size();
507 }
508
509 int PluginFClient_Opt::units(char *rp)
510 {
511         ArrayList<const AVOption *> opts;
512         int n = units(opts);
513         if( !n ) return 0;
514         char *cp = rp;
515         cp += sprintf(cp, " [%s:", this->opt->unit);
516         for( int i=0; i<n; ++i )
517                 cp += sprintf(cp, " %s", opts[i]->name);
518         cp += sprintf(cp, "]:");
519         return cp - rp;
520 }
521
522 const char *PluginFClient_Opt::tip()
523 {
524         return opt->help;
525 }
526
527 int PluginFClient_OptPanel::update()
528 {
529         const char *cols[] = { "option", "value", };
530         const int col1_w = xS(150);
531         int wids[] = { col1_w, get_w()-col1_w };
532         BC_ListBox::update(&items[0], &cols[0], &wids[0], sizeof(items)/sizeof(items[0]),
533                 get_xposition(), get_yposition(), get_highlighted_item());
534         return 0;
535 }
536
537
538 PluginFClientWindow::PluginFClientWindow(PluginFClient *ffmpeg)
539  : PluginClientWindow(ffmpeg->plugin, xS(600), yS(300), xS(600), yS(300), 1)
540 {
541         this->ffmpeg = ffmpeg;
542         this->selected = 0;
543 }
544
545 PluginFClientWindow::~PluginFClientWindow()
546 {
547 }
548
549 void PluginFClientWindow::create_objects()
550 {
551         int xs8 = xS(8), xs10 = xS(10), ys10 = yS(10);
552         char string[BCTEXTLEN];
553         BC_Title *title;
554         int x = xs10, y = ys10;
555         const char *descr = ffmpeg->config.ffilt->description();
556         if( !descr ) descr = ffmpeg->config.ffilt->filter_name();
557         add_subwindow(title = new BC_Title(x, y, descr));
558         y += title->get_h() + ys10;
559         int x0 = x;
560         sprintf(string, _("Type: "));
561         add_subwindow(title = new BC_Title(x0, y, string));
562         x0 += title->get_w() + xs8;
563         add_subwindow(type = new BC_Title(x0, y, (char *)""));
564         x0 = x + xS(150);
565         sprintf(string, _("Range: "));
566         add_subwindow(title = new BC_Title(x0, y, string));
567         x0 += title->get_w() + xs8;
568         add_subwindow(range = new BC_Title(x0, y, (char *)""));
569         int x1 = get_w() - BC_GenericButton::calculate_w(this, _("Reset")) - xs8;
570         add_subwindow(reset = new PluginFClientReset(this, x1, y));
571         y += title->get_h() + ys10;
572         x0 = x;
573         add_subwindow(units = new PluginFClientUnits(this, x0, y, xS(120)));
574         x0 += units->get_w() + xs8;
575         x1 = get_w() - BC_GenericButton::calculate_w(this, _("Apply")) - xs8;
576         add_subwindow(apply = new PluginFClientApply(this, x1, y));
577         add_subwindow(text = new PluginFClientText(this, x0, y, x1-x0 - xs8));
578         y += title->get_h() + ys10;
579         add_subwindow(pot = new PluginFClientPot(this, x, y));
580         x1 = x + pot->get_w() + xs10;
581         add_subwindow(slider = new PluginFClientSlider(this, x1, y+ys10));
582         y += pot->get_h() + ys10;
583
584         panel_x = x;  panel_y = y;
585         panel_w = get_w()-xs10 - panel_x;
586         panel_h = get_h()-ys10 - panel_y;
587         panel = new PluginFClient_OptPanel(this, panel_x, panel_y, panel_w, panel_h);
588         add_subwindow(panel);
589         panel->create_objects();
590         ffmpeg->config.update();
591         draw();
592         show_window(1);
593 }
594
595 void PluginFClientWindow::draw()
596 {
597         update(selected);
598 }
599
600 int PluginFClientWindow::resize_event(int w, int h)
601 {
602         int xs8 = xS(8), xs10 = xS(10), ys10 = yS(10);
603         int x = get_w() - BC_GenericButton::calculate_w(this, _("Reset")) - xs8;
604         int y = reset->get_y();
605         reset->reposition_window(x, y);
606         int x1 = get_w() - BC_GenericButton::calculate_w(this, _("Apply")) - xs8;
607         int y1 = units->get_y();
608         apply->reposition_window(x1, y1);
609         int x0 = units->get_x() + units->get_w() + xs8;
610         int y0 = units->get_y();
611         text->reposition_window(x0,y0, x1-x0-xs8);
612         x1 = pot->get_x() + pot->get_w() + xs10;
613         int w1 = w - slider->get_x() - xS(20);
614         slider->set_pointer_motion_range(w1);
615         slider->reposition_window(x1, slider->get_y(), w1, slider->get_h());
616         panel_w = get_w()-xs10 - panel_x;
617         panel_h = get_h()-ys10 - panel_y;
618         panel->reposition_window(panel_x,panel_y, panel_w, panel_h);
619         return 1;
620 }
621
622
623 PluginFClient::PluginFClient(PluginClient *plugin, const char *name)
624 {
625         this->plugin = plugin;
626         this->name = name;
627         ffilt = 0;
628         fsrc = fsink = 0;
629         plugin_position = -1;
630         filter_position = -1;
631         activated = 0;
632         sprintf(title, "F_%s", name);
633         config.initialize(name);
634         curr_config.initialize(name);
635 }
636
637 PluginFClient::~PluginFClient()
638 {
639         delete ffilt;
640 }
641
642 bool PluginFClient::is_audio(const AVFilter *fp)
643 {
644         if( !fp->outputs ) return 0;
645         if( avfilter_pad_count(fp->outputs) > 1 ) return 0;
646         if( !avfilter_pad_get_name(fp->outputs, 0) ) return 0;
647         if( avfilter_pad_get_type(fp->outputs, 0) != AVMEDIA_TYPE_AUDIO ) return 0;
648         if( !fp->inputs ) return 1;
649         if( avfilter_pad_count(fp->inputs) > 1 ) return 0;
650         if( !avfilter_pad_get_name(fp->inputs, 0) ) return 0;
651         if( avfilter_pad_get_type(fp->inputs, 0) != AVMEDIA_TYPE_AUDIO ) return 0;
652         return 1;
653 }
654 bool PluginFClient::is_video(const AVFilter *fp)
655 {
656         if( !fp->outputs ) return 0;
657         if( avfilter_pad_count(fp->outputs) > 1 ) return 0;
658         if( !avfilter_pad_get_name(fp->outputs, 0) ) return 0;
659         if( avfilter_pad_get_type(fp->outputs, 0) != AVMEDIA_TYPE_VIDEO ) return 0;
660         if( !fp->inputs ) return 1;
661         if( avfilter_pad_count(fp->inputs) > 1 ) return 0;
662         if( !avfilter_pad_get_name(fp->inputs, 0) ) return 0;
663         if( avfilter_pad_get_type(fp->inputs, 0) != AVMEDIA_TYPE_VIDEO ) return 0;
664         return 1;
665 }
666
667 NEW_WINDOW_MACRO(PluginFClient, PluginFClientWindow)
668
669 int PluginFClient::load_configuration()
670 {
671         int64_t src_position =  get_source_position();
672         KeyFrame *prev_keyframe = get_prev_keyframe(src_position);
673         config.copy_from(curr_config);
674         read_data(prev_keyframe);
675         int ret = !config.equivalent(curr_config) ? 1 : 0;
676         return ret;
677 }
678
679
680 void PluginFClient::render_gui(void *data, int size)
681 {
682         PluginFClientConfig *conf = (PluginFClientConfig *)data;
683         config.copy_from(*conf);
684         KeyFrame *keyframe = plugin->server->get_keyframe();
685         save_data(keyframe);
686         update_gui();
687 }
688
689 void PluginFClient::update_gui()
690 {
691         PluginClientThread *thread = plugin->get_thread();
692         if( !thread ) return;
693         PluginFClientWindow *window = (PluginFClientWindow*)thread->get_window();
694         window->lock_window("PluginFClient::update_gui");
695         load_configuration();
696         if( config.update() > 0 )
697                 window->draw();
698         window->unlock_window();
699 }
700
701 const char *PluginFClient::plugin_title()
702 {
703         return title;
704 }
705
706 char *PluginFClient::to_upper(char *cp, const char *sp)
707 {
708         char *bp = cp;
709         while( *sp != 0 ) *bp++ = toupper(*sp++);
710         *bp = 0;
711         return cp;
712 }
713
714 void PluginFClient::save_data(KeyFrame *keyframe)
715 {
716         FileXML output;
717         char string[BCTEXTLEN];
718
719 // cause data to be stored directly in text
720         output.set_shared_output(keyframe->xbuf);
721         output.tag.set_title(to_upper(string, plugin_title()));
722         const AVClass *filt_class = config.filter_class();
723         if( filt_class && filt_class->option ) {
724                 void *obj = config.filter_config();
725                 const AVOption *opt = NULL;
726                 while( (opt=av_opt_next(obj, opt)) != 0 ) {
727                         uint8_t *buf = 0;
728                         if( av_opt_get(obj, opt->name, 0, &buf) < 0 ) continue;
729                         output.tag.set_property(opt->name, (const char *)buf);
730                         av_freep(&buf);
731                 }
732         }
733         if( (config.ffilt->filter->flags & AVFILTER_FLAG_SLICE_THREADS) != 0 ) {
734                 uint8_t *buf = 0;
735                 if( av_opt_get(config.ffilt->fctx, "threads", 0, &buf) >= 0 && buf ) {
736                         output.tag.set_property("threads", (const char *)buf);
737                         av_freep(&buf);
738                 }
739         }
740
741         output.append_tag();
742         output.terminate_string();
743 }
744
745 void PluginFClient::read_data(KeyFrame *keyframe)
746 {
747         FileXML input;
748         char string[BCTEXTLEN];
749         input.set_shared_input(keyframe->xbuf);
750
751         while( !input.read_tag() ) {
752                 to_upper(string, plugin_title());
753                 if( !input.tag.title_is(string) ) continue;
754                 const AVClass *filt_class = config.filter_class();
755                 if( filt_class && filt_class->option ) {
756                         void *obj = config.filter_config();
757                         const AVOption *opt = NULL;
758                         while( (opt=av_opt_next(obj, opt)) != 0 ) {
759                                 const char *v = input.tag.get_property(opt->name);
760                                 if( v ) av_opt_set(obj, opt->name, v, 0);
761                         }
762                         if( (config.ffilt->filter->flags & AVFILTER_FLAG_SLICE_THREADS) != 0 ) {
763                                 const char *v = input.tag.get_property("threads");
764                                 if( v ) av_opt_set(config.ffilt->fctx, "threads", v, 0);
765                         }
766                 }
767         }
768 }
769
770 int PluginFClient::activate()
771 {
772         if( fsrc )
773                 avfilter_link(fsrc, 0, ffilt->fctx, 0);
774         avfilter_link(ffilt->fctx, 0, fsink, 0);
775         int ret = avfilter_graph_config(ffilt->graph, NULL);
776         if( ret >= 0 ) {
777                 curr_config.copy_from(config);
778                 activated = 1;
779         }
780         return ret;
781 }
782
783 void PluginFClient::reactivate()
784 {
785         delete ffilt;  ffilt = 0;
786         activated = 0;
787 }
788
789 PluginFAClient::PluginFAClient(PluginServer *server, const char *name)
790  : PluginAClient(server), PluginFClient(this, name)
791 {
792 }
793
794 PluginFAClient::~PluginFAClient()
795 {
796 }
797
798 int PluginFAClient::activate()
799 {
800         if( activated ) return activated;
801         ffilt = PluginFFilter::new_ffilter(name, &config);
802         if( !ffilt ) {
803                 config.copy_from(curr_config);
804                 send_configure_change();
805                 send_render_gui(&config, sizeof(config));
806                 ffilt = PluginFFilter::new_ffilter(name, &config);
807         }
808         AVSampleFormat sample_fmt = AV_SAMPLE_FMT_FLTP;
809         int channels = PluginClient::total_in_buffers;
810         uint64_t layout = (((uint64_t)1)<<channels) - 1;
811         AVFilterGraph *graph = !ffilt ? 0 : ffilt->graph;
812         int ret = !graph ? -1 : 0;
813         if( ret >= 0 && ffilt->filter->inputs ) {
814                 char args[BCTEXTLEN];
815                 snprintf(args, sizeof(args),
816                         "time_base=%d/%d:sample_rate=%d:sample_fmt=%s:channel_layout=0x%jx",
817                         1, sample_rate, sample_rate, av_get_sample_fmt_name(sample_fmt), layout);
818                 ret = avfilter_graph_create_filter(&fsrc, avfilter_get_by_name("abuffer"),
819                         "in", args, NULL, graph);
820         }
821         if( ret >= 0 )
822                 ret = avfilter_graph_create_filter(&fsink, avfilter_get_by_name("abuffersink"),
823                         "out", NULL, NULL, graph);
824         if( ret >= 0 )
825                 ret = av_opt_set_bin(fsink, "sample_fmts",
826                         (uint8_t*)&sample_fmt, sizeof(sample_fmt), AV_OPT_SEARCH_CHILDREN);
827         if( ret >= 0 )
828                 ret = av_opt_set_bin(fsink, "channel_layouts",
829                         (uint8_t*)&layout, sizeof(layout), AV_OPT_SEARCH_CHILDREN);
830         if( ret >= 0 )
831                 ret = av_opt_set_bin(fsink, "sample_rates",
832                         (uint8_t*)&sample_rate, sizeof(sample_rate), AV_OPT_SEARCH_CHILDREN);
833         if( ret >= 0 )
834                 ret = PluginFClient::activate();
835         if( ret < 0 && activated >= 0 ) {
836                 ff_err(ret, "PluginFAClient::activate: %s failed\n", plugin_title());
837                 activated = -1;
838         }
839         return activated;
840 }
841
842
843 static AVRational best_frame_rate(double frame_rate)
844 {
845         static const int m1 = 1001*12, m2 = 1000*12;
846         static const int freqs[] = {
847                 40*m1, 48*m1, 50*m1, 60*m1, 80*m1,120*m1, 240*m1,
848                 24*m2, 30*m2, 60*m2, 12*m2, 15*m2, 48*m2, 90*m2,
849                 100*m2, 120*m2, 144*m2, 72*m2, 0,
850         };
851         double max_err = 1.;
852         int freq, best_freq = 0;
853         for( int i=0; (freq = i<30*12 ? (i+1)*1001 : freqs[i-30*12]); ++i ) {
854                 double framerate = (double)freq / m1;
855                 double err = fabs(frame_rate/framerate - 1.);
856                 if( err >= max_err ) continue;
857                 max_err = err;
858                 best_freq = freq;
859         }
860         return max_err < 0.0001 ?
861                 (AVRational) { best_freq, m1 } :
862                 (AVRational) { 0, 0 };
863 }
864
865 int PluginFVClient::activate(int width, int height, int color_model)
866 {
867         if( activated ) return activated;
868         ffilt = PluginFFilter::new_ffilter(name, &config);
869         if( !ffilt ) {
870                 config.copy_from(curr_config);
871                 send_configure_change();
872                 send_render_gui(&config, sizeof(config));
873                 ffilt = PluginFFilter::new_ffilter(name, &config);
874         }
875         AVPixelFormat pix_fmt = color_model_to_pix_fmt(color_model);
876         AVFilterGraph *graph = !ffilt ? 0 : ffilt->graph;
877         int ret = !graph ? -1 : 0;
878         if( ret >= 0 && ffilt->filter->inputs ) {
879                 curr_config.copy_from(config);
880                 if( pix_fmt == AV_PIX_FMT_NB ) {
881                         int bpp = BC_CModels::calculate_pixelsize(color_model) * 8;
882                         int bits_per_comp = bpp / BC_CModels::components(color_model);
883                         int alpha =  BC_CModels::has_alpha(color_model);
884                         pix_fmt = bits_per_comp > 8 ?
885                                 !alpha ? AV_PIX_FMT_RGB48LE : AV_PIX_FMT_RGBA64LE :
886                                 !alpha ? AV_PIX_FMT_RGB24 : AV_PIX_FMT_RGBA ;
887                 }
888                 int aspect_w = 1, aspect_h = 1; // XXX
889                 AVRational best_rate = best_frame_rate(frame_rate);
890                 char args[BCTEXTLEN];
891                 snprintf(args, sizeof(args),
892                         "video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d",
893                         width, height, pix_fmt, best_rate.den, best_rate.num, aspect_w, aspect_h);
894                 ret = avfilter_graph_create_filter(&fsrc, avfilter_get_by_name("buffer"),
895                         "in", args, NULL, graph);
896         }
897         if( ret >= 0 )
898                 ret = avfilter_graph_create_filter(&fsink, avfilter_get_by_name("buffersink"),
899                         "out", NULL, NULL, graph);
900         if( ret >= 0 )
901                 ret = av_opt_set_bin(fsink, "pix_fmts",
902                         (uint8_t*)&pix_fmt, sizeof(pix_fmt), AV_OPT_SEARCH_CHILDREN);
903         if( ret >= 0 )
904                 ret = PluginFClient::activate();
905         if( ret < 0 && activated >= 0 ) {
906                 ff_err(ret, "PluginFVClient::activate() %s\n", plugin_title());
907                 activated = -1;
908         }
909         return activated;
910 }
911
912 int PluginFAClient::get_inchannels()
913 {
914         AVFilterContext *fctx = ffilt->fctx;
915         AVFilterLink **links = !fctx->nb_inputs ? 0 : fctx->inputs;
916 #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(59,24,100)
917         return !links ? 0 : links[0]->ch_layout.nb_channels;
918 #else
919         return !links ? 0 : links[0]->channels;
920 #endif
921 }
922
923 int PluginFAClient::get_outchannels()
924 {
925         AVFilterContext *fctx = ffilt->fctx;
926         AVFilterLink **links = !fctx->nb_outputs ? 0 : fctx->outputs;
927 #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(59,24,100)
928         return !links ? 0 : links[0]->ch_layout.nb_channels;
929 #else
930         return !links ? 0 : links[0]->channels;
931 #endif
932 }
933
934 int PluginFAClient::process_buffer(int64_t size, Samples **buffer, int64_t start_position, int sample_rate)
935 {
936         int total_in = PluginClient::total_in_buffers;
937         int total_out = PluginClient::total_out_buffers;
938         int in_channels = 0, out_channels = 0;
939
940         if( load_configuration() )
941                 reactivate();
942
943         if( plugin_position != start_position )
944                 filter_position = plugin_position = start_position;
945
946         AVFrame *frame = 0;
947         int ret = activate();
948         if( ret >= 0 && !(frame = av_frame_alloc()) ) {
949                 fprintf(stderr, "PluginFAClient::process_buffer: av_frame_alloc failed\n");
950                 ret = AVERROR(ENOMEM);
951         }
952         if( ret >= 0 ) {
953                 in_channels = get_inchannels();
954                 out_channels = get_outchannels();
955                 frame->nb_samples = size;
956                 frame->format = AV_SAMPLE_FMT_FLTP;
957                 frame->channel_layout = (1<<in_channels)-1;
958                 frame->sample_rate = sample_rate;
959                 frame->pts = filter_position;
960         }
961
962         int retry = 10;
963         while( ret >= 0 && --retry >= 0 ) {
964                 ret = av_buffersink_get_frame(fsink, frame);
965                 if( ret >= 0 || ret != AVERROR(EAGAIN) ) break;
966                 if( !fsrc ) { ret = AVERROR(EIO);  break; }
967                 for( int i=0; i<total_in; ++i )
968                         read_samples(buffer[i], i, sample_rate, filter_position, size);
969                 filter_position += size;
970                 ret = av_frame_get_buffer(frame, 0);
971                 if( ret < 0 ) break;
972                 float **in_buffers = (float **)&frame->extended_data[0];
973                 for(int i = 0; i < in_channels; i++) {
974                         float *in_buffer = in_buffers[i];
975                         int k = i < total_in ? i : 0;
976                         double *in_ptr = buffer[k]->get_data();
977                         for(int j = 0; j < size; j++) in_buffer[j] = in_ptr[j];
978                 }
979                 ret = av_buffersrc_add_frame_flags(fsrc, frame, 0);
980         }
981         if( ret >= 0 && retry < 0 )
982                 ret = AVERROR(EAGAIN);
983
984         if( ret >= 0 ) {
985                 int nbfrs = total_out;
986                 if( out_channels < nbfrs ) nbfrs = out_channels;
987                 float **out_buffers = (float **)&frame->extended_data[0];
988                 int i = 0;
989                 while( i < nbfrs ) {
990                         float *out_buffer = out_buffers[i];
991                         double *out_ptr = buffer[i++]->get_data();
992                         for(int j = 0; j < size; j++) out_ptr[j] = out_buffer[j];
993                 }
994                 while( i < total_out ) {
995                         double *out_ptr = buffer[i++]->get_data();
996                         bzero(out_ptr, sizeof(double) * size);
997                 }
998         }
999         if( ret < 0 ) {
1000                 int64_t position = PluginFClient::get_source_position();
1001                 double t0 = (double)position/sample_rate, dt = 1./sample_rate;
1002                 for( int i=0; i<total_out; ++i ) {
1003                         double t = t0, *out = buffer[i]->get_data();
1004                         for( int k=size; --k>=0; t+=dt ) {
1005                                 double w = int(2*t) & 1 ? 2*M_PI*440 : 2*M_PI*697;
1006                                 *out++ = sin(t * w);
1007                         }
1008                 }
1009                 ff_err(ret, "PluginFAClient::process_buffer() %s\n", plugin_title());
1010         }
1011
1012         av_frame_free(&frame);
1013         plugin_position += size;
1014         return size;
1015 }
1016
1017
1018 PluginFVClient::PluginFVClient(PluginServer *server, const char *name)
1019  : PluginVClient(server),
1020    PluginFClient(this, name),
1021    FFVideoConvert(server->preferences)
1022 {
1023 }
1024
1025 PluginFVClient::~PluginFVClient()
1026 {
1027 }
1028
1029 int PluginFVClient::process_buffer(VFrame **frames, int64_t position, double frame_rate)
1030 {
1031         VFrame *vframe = *frames;
1032         int width = vframe->get_w();
1033         int height = vframe->get_h();
1034
1035         if( load_configuration() )
1036                 reactivate();
1037
1038         if( plugin_position != position )
1039                 filter_position = plugin_position = position;
1040
1041         int colormodel = vframe->get_color_model();
1042         int ret = activate(width, height, colormodel);
1043         AVPixelFormat pix_fmt = fsrc ?
1044                 (AVPixelFormat) fsrc->outputs[0]->format :
1045                 color_model_to_pix_fmt(colormodel);
1046         if( pix_fmt <= AV_PIX_FMT_NONE || pix_fmt >= AV_PIX_FMT_NB )
1047                 pix_fmt = AV_PIX_FMT_RGBA;
1048
1049         AVFrame *frame = 0;
1050         if( ret >= 0 && !(frame = av_frame_alloc()) ) {
1051                 fprintf(stderr, "PluginFVClient::process_buffer: av_frame_alloc failed\n");
1052                 ret = AVERROR(ENOMEM);
1053         }
1054
1055         int retry = 10;
1056         while( ret >= 0 && --retry >= 0 ) {
1057                 ret = av_buffersink_get_frame(fsink, frame);
1058                 if( ret >= 0 || ret != AVERROR(EAGAIN) ) break;
1059                 if( !fsrc ) { ret = AVERROR(EIO);  break; }
1060                 read_frame(vframe, 0, filter_position, frame_rate, 0);
1061                 frame->format = pix_fmt;
1062                 frame->width  = width;
1063                 frame->height = height;
1064                 frame->pts = filter_position++;
1065                 ret = av_frame_get_buffer(frame, 32);
1066                 if( ret < 0 ) break;
1067                 ret = transfer_pixfmt(vframe, frame);
1068                 if( ret < 0 ) break;
1069                 ret = av_buffersrc_add_frame_flags(fsrc, frame, 0);
1070         }
1071         if( ret >= 0 && retry < 0 )
1072                 ret = AVERROR(EAGAIN);
1073
1074         if( ret >= 0 ) {
1075                 pix_fmt = (AVPixelFormat) frame->format;
1076                 ret = transfer_cmodel(vframe, frame);
1077         }
1078         if( ret < 0 ) {
1079                 ff_err(ret, "PluginFVClient::process_buffer() %s\n", plugin_title());
1080                 if( position & 1 ) vframe->clear_frame();
1081         }
1082
1083         ++plugin_position;
1084         av_frame_free(&frame);
1085         return ret >= 0 ? 0 : 1;
1086 }
1087
1088
1089 PluginFClient_OptName:: PluginFClient_OptName(PluginFClient_Opt *opt)
1090 {
1091         this->opt = opt;
1092         set_text(opt->opt->name);
1093 }
1094
1095 PluginFClient_OptValue::PluginFClient_OptValue(PluginFClient_Opt *opt)
1096 {
1097         this->opt = opt;
1098         update();
1099 }
1100
1101 void PluginFClient_OptValue::update()
1102 {
1103         char val[BCTEXTLEN];  val[0] = 0;
1104         opt->get(val, sizeof(val));
1105         set_text(val);
1106 }
1107
1108
1109 PluginFClient_Opt::PluginFClient_Opt(PluginFClientConfig *conf, const AVOption *opt)
1110 {
1111         this->conf = conf;
1112         this->opt = opt;
1113         item_name = new PluginFClient_OptName(this);
1114         item_value = new PluginFClient_OptValue(this);
1115 }
1116
1117 PluginFClient_Opt::~PluginFClient_Opt()
1118 {
1119         delete item_name;
1120         delete item_value;
1121 }
1122
1123 const char *PluginFClientConfig::get(const char *name)
1124 {
1125         uint8_t *bp = 0;
1126         if( av_opt_get(filter_config(), name, 0, &bp) >= 0 ||
1127             av_opt_get(ffilt->fctx, name, 0, &bp) >= 0 )
1128                 return (const char *)bp;
1129         return 0;
1130 }
1131 char *PluginFClient_Opt::get(char *vp, int sz)
1132 {
1133         const char *val = conf->get(opt->name);
1134         if( val ) {
1135                 strncpy(vp, val, sz);
1136                 vp[sz-1] = 0;
1137         }
1138         else
1139                 vp = 0;
1140         av_freep(&val);
1141         return vp;
1142 }
1143
1144 void PluginFClientConfig::set(const char *name, const char *val)
1145 {
1146         if( av_opt_set(filter_config(), name, val, 0) < 0 )
1147                 av_opt_set(ffilt->fctx, name, val, 0);
1148 }
1149 void PluginFClient_Opt::set(const char *val)
1150 {
1151         conf->set(opt->name, val);
1152 }
1153
1154 void PluginFFilter::uninit()
1155 {
1156 }
1157
1158 static int get_defaults(const char *name, char *args)
1159 {
1160         *args = 0;
1161         char defaults_path[BCTEXTLEN];
1162         FFMPEG::set_option_path(defaults_path, "plugin.opts");
1163         FILE *fp = fopen(defaults_path,"r");
1164         if( !fp ) return 0;
1165         char ff_plugin[BCSTRLEN], ff_args[BCTEXTLEN], *ap = 0;
1166         while( fgets(ff_args, sizeof(ff_args), fp) ) {
1167                 char *cp = ff_args;
1168                 if( *cp == ';' ) continue;
1169                 if( *cp == '#' ) ++cp;
1170                 char *bp = ff_plugin, *ep = bp + sizeof(ff_plugin)-1;
1171                 while( bp < ep && *cp && *cp != '\n' && *cp != ' ' ) *bp++ = *cp++;
1172                 *bp = 0;
1173                 if( !strcmp(ff_plugin, name) ) { ap = cp;  break; }
1174         }
1175         fclose(fp);
1176         if( !ap ) return 0;
1177         if( ff_args[0] == '#' ) return -1;
1178         while( *ap == ' ' ) ++ap;
1179         while( *ap && *ap != '\n' ) *args++ = *ap++;
1180         *args = 0;
1181         return 1;
1182 }
1183
1184 bool PluginFFilter::is_audio()
1185 {
1186         return PluginFClient::is_audio(filter);
1187 }
1188
1189 bool PluginFFilter::is_video()
1190 {
1191         return PluginFClient::is_video(filter);
1192 }
1193
1194 int PluginFFilter::init(const char *name, PluginFClientConfig *conf)
1195 {
1196         char args[BCTEXTLEN];
1197         int ret = get_defaults(name, args);
1198         if( ret < 0 ) return 0;
1199         PluginFLogLevel errs(AV_LOG_ERROR);
1200         this->filter = avfilter_get_by_name(name);
1201         if( !this->filter ) return AVERROR(ENOENT);
1202         int flag_mask = AVFILTER_FLAG_DYNAMIC_INPUTS | AVFILTER_FLAG_DYNAMIC_OUTPUTS;
1203         if( filter->flags & flag_mask ) return AVERROR(EPERM);
1204         if( !this->is_audio() && !this->is_video() ) return AVERROR(EIO);
1205         this->graph = avfilter_graph_alloc();
1206         if( !this->graph ) return AVERROR(ENOMEM);
1207         static int inst = 0;
1208         char inst_name[BCSTRLEN];
1209         sprintf(inst_name,"%s_%d", name, ++inst);
1210         if( conf && (filter->flags & AVFILTER_FLAG_SLICE_THREADS) != 0 ) {
1211                 graph->thread_type = AVFILTER_THREAD_SLICE;
1212                 graph->nb_threads  = atoi(conf->get("threads"));
1213         }
1214         else {
1215                 graph->thread_type = 0;
1216                 graph->nb_threads  = 0;
1217         }
1218         fctx = avfilter_graph_alloc_filter(graph, filter, inst_name);
1219         if( !fctx ) return AVERROR(ENOMEM);
1220         fctx->thread_type = graph->thread_type; // bug in avfilter
1221         if( conf ) {
1222                 AVDictionary *opts = 0;
1223                 for( int i=0; i<conf->size(); ++i ) {
1224                         PluginFClient_Opt *op = conf->get(i);
1225                         const char *name = op->opt->name;
1226                         char val[BCTEXTLEN], *vp = op->get(val, sizeof(val));
1227                         if( !vp ) continue;
1228                         uint8_t *bp = 0;
1229 // unspecified opts cause a special behavior in some filters (lut3d)
1230 // so... if opt value is the default, skip it or no special behavior
1231                         if( av_opt_get(filter_config(), name, 0, &bp) >= 0 ) {
1232                                 int result = strcmp((const char *)bp, vp);
1233                                 av_freep(&bp);
1234                                 if( !result ) continue;
1235                         }
1236                         av_dict_set(&opts, name, vp, 0);
1237                 }
1238                 ret = avfilter_init_dict(fctx, &opts);
1239                 av_dict_free(&opts);
1240         }
1241         else
1242                 ret = avfilter_init_str(fctx, args);
1243         return ret < 0 ? ret : 1;
1244 }
1245
1246
1247 PluginFFilter::PluginFFilter()
1248 {
1249         filter = 0;
1250         graph = 0;
1251         fctx = 0;
1252 }
1253
1254 PluginFFilter::~PluginFFilter()
1255 {
1256         PluginFLogLevel errs(AV_LOG_ERROR);
1257         avfilter_graph_free(&graph);
1258         filter = 0;  fctx = 0;
1259 }
1260
1261 PluginFFilter *PluginFFilter::new_ffilter(const char *name, PluginFClientConfig *conf)
1262 {
1263         PluginFFilter *ffilt = new PluginFFilter;
1264         int ret = ffilt->init(name, conf);
1265         if( ret < 0 ) ff_err(ret, "PluginFFilter::new_ffilter(%s)\n", name);
1266         if( ret <= 0 ) { delete ffilt;  ffilt = 0; }
1267         return ffilt;
1268 }
1269
1270 PluginClient *PluginServer::new_ffmpeg_plugin()
1271 {
1272         const AVFilter *filter = avfilter_get_by_name(ff_name);
1273         if( !filter ) return 0;
1274         PluginFClient *ffmpeg =
1275                 PluginFClient::is_audio(filter) ?
1276                         (PluginFClient *)new PluginFAClient(this, ff_name) :
1277                 PluginFClient::is_video(filter) ?
1278                         (PluginFClient *)new PluginFVClient(this, ff_name) :
1279                 0;
1280         if( !ffmpeg ) return 0;
1281         return ffmpeg->plugin;
1282 }
1283
1284
1285 PluginServer* MWindow::new_ffmpeg_server(MWindow *mwindow, const char *name)
1286 {
1287         PluginFFilter *ffilt = PluginFFilter::new_ffilter(name);
1288         if( !ffilt ) return 0;
1289         delete ffilt;
1290         return new PluginServer(mwindow, name, PLUGIN_TYPE_FFMPEG);
1291 }
1292
1293 void MWindow::init_ffmpeg_index(MWindow *mwindow, Preferences *preferences, FILE *fp)
1294 {
1295         PluginFLogLevel errs(AV_LOG_ERROR);
1296         const AVFilter *filter = 0;  void *idx = 0;
1297         while( (filter=av_filter_iterate(&idx)) != 0 ) {
1298 //printf("%s\n",filter->name);
1299                 PluginServer *server = new_ffmpeg_server(mwindow, filter->name);
1300                 if( server ) {
1301                         int result = server->open_plugin(1, preferences, 0, 0);
1302                         if( !result ) {
1303                                 server->write_table(fp, filter->name, PLUGIN_FFMPEG_ID, 0);
1304                                 server->close_plugin();
1305                         }
1306                         delete server;
1307                         if( result ) fprintf(fp, "#%s\n", filter->name);
1308                 }
1309         }
1310 }
1311
1312 void MWindow::init_ffmpeg()
1313 {
1314 }
1315