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