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