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