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