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