3 * Copyright (C) 2016-2020 William Morrow
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published
7 * by the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
15 * You should have received a copy of the GNU General Public
16 * License along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
28 #include "bcwindowbase.h"
34 #include "pluginfclient.h"
35 #include "pluginserver.h"
40 #include "libavfilter/version.h"
43 #define av_filter_iterate(p) ((*(const AVFilter**)(p))=avfilter_next(*(const AVFilter **)(p)))
46 static void ff_err(int ret, const char *fmt, ...)
51 vsnprintf(msg, sizeof(msg), fmt, ap);
53 char errmsg[BCSTRLEN];
54 av_strerror(ret, errmsg, sizeof(errmsg));
55 fprintf(stderr,_("%s err: %s\n"),msg, errmsg);
58 PluginFClientConfig::PluginFClientConfig()
63 PluginFClientConfig::~PluginFClientConfig()
69 int PluginFClientConfig::equivalent(PluginFClientConfig &that)
71 PluginFClientConfig &conf = *this;
72 for( int i=0; i<that.size(); ++i ) {
73 PluginFClient_Opt *topt = conf[i], *vopt = that[i];
74 char tval[BCTEXTLEN], *tp = topt->get(tval, sizeof(tval));
75 char vval[BCTEXTLEN], *vp = vopt->get(vval, sizeof(vval));
76 int ret = tp && vp ? strcmp(tp, vp) : tp || vp ? 1 : 0;
82 void PluginFClientConfig::copy_from(PluginFClientConfig &that)
84 PluginFClientConfig &conf = *this;
85 for( int i=0; i<that.size(); ++i ) {
86 PluginFClient_Opt *vp = that[i];
87 const char *nm = vp->opt->name;
89 while( --k >= 0 && strcmp(nm, conf[k]->opt->name) );
91 PluginFClient_Opt *fopt = conf[k];
92 char str[BCTEXTLEN], *sp = vp->get(str, sizeof(str));
93 if( sp ) fopt->set(sp);
97 void PluginFClientConfig::interpolate(PluginFClientConfig &prev, PluginFClientConfig &next,
98 int64_t prev_frame, int64_t next_frame, int64_t current_frame)
103 void PluginFClientConfig::initialize(const char *name)
106 ffilt = PluginFFilter::new_ffilter(name);
107 const AVOption *opt = 0;
108 void *obj = ffilt->filter_config();
110 const AVClass *filt_class = ffilt->filter_class();
111 if( filt_class && filt_class->option ) {
112 PluginFClientConfig &conf = *this;
113 while( (opt=av_opt_next(obj, opt)) != 0 ) {
114 if( opt->type == AV_OPT_TYPE_CONST ) continue;
116 for( int i=0; !dupl && i<size(); ++i ) {
117 PluginFClient_Opt *fp = conf[i];
118 const AVOption *op = fp->opt;
119 if( op->offset != opt->offset ) continue;
120 if( op->type != opt->type ) continue;
122 if( strlen(op->name) < strlen(opt->name) )
126 PluginFClient_Opt *fopt = new PluginFClient_Opt(this, opt);
130 if( (ffilt->filter->flags & AVFILTER_FLAG_SLICE_THREADS) != 0 ) {
131 opt = av_opt_find(ffilt->fctx, "threads", NULL, 0, 0);
133 PluginFClient_Opt *fopt = new PluginFClient_Opt(this, opt);
139 int PluginFClientConfig::update()
142 PluginFClientConfig &conf = *this;
144 for( int i=0; i<size(); ++i ) {
145 PluginFClient_Opt *fopt = conf[i];
146 char val[BCTEXTLEN], *vp = fopt->get(val, sizeof(val));
147 if( !vp || !strcmp(val, fopt->item_value->get_text()) ) continue;
148 fopt->item_value->update();
154 void PluginFClientConfig::dump(FILE *fp)
156 const AVOption *opt = 0;
157 const AVClass *obj = filter_class();
158 PluginFClientConfig &conf = *this;
160 while( (opt=av_opt_next(&obj, opt)) != 0 ) {
161 if( opt->type == AV_OPT_TYPE_CONST ) continue;
163 while( --k >= 0 && strcmp(opt->name, conf[k]->opt->name) );
164 if( k < 0 ) continue;
165 PluginFClient_Opt *fopt = conf[k];
166 char val[BCTEXTLEN], *vp = fopt->get(val,sizeof(val));
167 fprintf(fp, " %s:=%s", opt->name, vp);
169 char unt[BCTEXTLEN], *up = unt;
171 fprintf(fp, "%s", unt);
178 PluginFClientReset(PluginFClientWindow *fwin, int x, int y)
179 : BC_GenericButton(x, y, _("Reset"))
185 ~PluginFClientReset()
189 int PluginFClientReset::handle_event()
191 fwin->ffmpeg->config.initialize(fwin->ffmpeg->name);
192 if( fwin->ffmpeg->config.update() > 0 )
194 fwin->ffmpeg->plugin->send_configure_change();
199 PluginFClientText(PluginFClientWindow *fwin, int x, int y, int w)
200 : BC_TextBox(x, y, w, 1, (char *)"")
210 int PluginFClientText::handle_event()
212 if( get_keypress() == RETURN ) {
220 PluginFClientUnits(PluginFClientWindow *fwin, int x, int y, int w)
221 : BC_PopupMenu(x, y, w, "")
227 ~PluginFClientUnits()
231 int PluginFClientUnits::handle_event()
233 const char *text = get_text();
234 if( text && fwin->selected ) {
235 if( text ) fwin->selected->set(text);
236 fwin->selected->item_value->update();
238 fwin->ffmpeg->plugin->send_configure_change();
244 PluginFClientApply(PluginFClientWindow *fwin, int x, int y)
245 : BC_GenericButton(x, y, _("Apply"))
251 ~PluginFClientApply()
255 int PluginFClientApply::handle_event()
257 return fwin->update();
260 PluginFClientPot::PluginFClientPot(PluginFClientWindow *fwin, int x, int y)
261 : BC_FPot(x, y, 0.f, 0.f, 0.f)
266 int PluginFClient_Opt::get_range(float &fmn, float &fmx)
268 switch( opt->type ) {
269 case AV_OPT_TYPE_INT:
270 case AV_OPT_TYPE_INT64:
271 case AV_OPT_TYPE_DOUBLE:
272 case AV_OPT_TYPE_FLOAT: break;
275 const AVClass *filt_class = filter_class();
276 if( !filt_class || !filt_class->option ) return 1;
277 AVOptionRanges *r = 0;
278 void *obj = &filt_class;
279 if( av_opt_query_ranges(&r, obj, opt->name, AV_OPT_SEARCH_FAKE_OBJ) < 0 ) return 1;
281 if( r->nb_ranges == 1 ) {
282 fmn = r->range[0]->value_min;
283 fmx = r->range[0]->value_max;
286 av_opt_freep_ranges(&r);
290 float PluginFClient_Opt::get_float()
292 char val[BCTEXTLEN]; val[0] = 0;
293 get(val, sizeof(val));
297 void PluginFClient_Opt::set_float(float v)
299 char val[BCTEXTLEN]; val[0] = 0;
300 sprintf(val, "%f", v);
304 int PluginFClientPot::handle_event()
306 if( fwin->selected ) {
307 fwin->selected->set_float(get_value());
308 fwin->update_selected();
309 return fwin->update();
314 PluginFClientSlider::PluginFClientSlider(PluginFClientWindow *fwin, int x, int y)
315 : BC_FSlider(x, y, 0, fwin->get_w()-x-xS(20), fwin->get_w()-x-xS(20), 0.f, 0.f, 0.f)
320 int PluginFClientSlider::handle_event()
322 if( fwin->selected ) {
323 fwin->selected->set_float(get_value());
324 fwin->update_selected();
325 return fwin->update();
331 PluginFClient_OptPanel::
332 PluginFClient_OptPanel(PluginFClientWindow *fwin, int x, int y, int w, int h)
333 : BC_ListBox(x, y, w, h, LISTBOX_TEXT), opts(items[0]), vals(items[1])
336 update(); // init col/wid/columns
339 PluginFClient_OptPanel::
340 ~PluginFClient_OptPanel()
344 void PluginFClient_OptPanel::create_objects()
346 PluginFClientConfig &fconfig = fwin->ffmpeg->config;
347 for( int i=0; i<fconfig.size(); ++i ) {
348 PluginFClient_Opt *opt = fconfig[i];
349 opts.append(opt->item_name);
350 vals.append(opt->item_value);
354 int PluginFClient_OptPanel::cursor_leave_event()
360 void PluginFClientWindow::update(PluginFClient_Opt *opt)
362 if( selected != opt ) {
363 if( selected ) selected->item_name->set_selected(0);
365 if( selected ) selected->item_name->set_selected(1);
367 clear_box(0,0, 0,panel->get_y());
368 char str[BCTEXTLEN], *sp;
370 if( opt ) opt->types(sp);
373 if( opt ) opt->ranges(sp);
375 while( units->total_items() ) units->del_item(0);
376 ArrayList<const AVOption *> opts;
377 int n = !opt ? 0 : opt->units(opts);
378 for( int i=0; i<n; ++i )
379 units->add_item(new BC_MenuItem(opts[i]->name, 0));
380 char unit[BCSTRLEN]; strcpy(unit, "()");
381 if( units->total_items() && opt && opt->opt->unit )
382 strcpy(unit, opt->opt->unit);
383 units->set_text(unit);
384 char val[BCTEXTLEN]; val[0] = 0;
385 if( opt ) opt->get(val, sizeof(val));
388 float v = 0, fmn = 0, fmx = 0;
389 if( opt && !opt->get_range(fmn, fmx) ) v = atof(val);
390 float p = (fmx-fmn) / slider->get_w();
391 slider->set_precision(p);
392 slider->update(slider->get_w(), v, fmn, fmx);
393 pot->set_precision(p);
394 pot->update(v, fmn, fmx);
398 int PluginFClientWindow::update()
400 const char *txt = text->get_text();
401 if( txt && selected ) {
403 selected->item_value->update();
405 ffmpeg->plugin->send_configure_change();
410 void PluginFClientWindow::update_selected()
415 int PluginFClient_OptPanel::selection_changed()
417 PluginFClient_Opt *opt = 0;
418 BC_ListBoxItem *item = get_selection(0, 0);
420 PluginFClient_OptName *opt_name = (PluginFClient_OptName *)item;
424 fwin->panel->set_tooltip(!opt ? 0 : opt->tip());
425 fwin->panel->show_tooltip();
429 void *PluginFClient_Opt::filter_config()
431 return conf->filter_config();
433 const AVClass *PluginFClient_Opt::filter_class()
435 return conf->filter_class();
438 int PluginFClient_Opt::types(char *rp)
442 case AV_OPT_TYPE_FLAGS: cp = "<flags>"; break;
443 case AV_OPT_TYPE_INT: cp = "<int>"; break;
444 case AV_OPT_TYPE_INT64: cp = "<int64>"; break;
445 case AV_OPT_TYPE_DOUBLE: cp = "<double>"; break;
446 case AV_OPT_TYPE_FLOAT: cp = "<float>"; break;
447 case AV_OPT_TYPE_STRING: cp = "<string>"; break;
448 case AV_OPT_TYPE_RATIONAL: cp = "<rational>"; break;
449 case AV_OPT_TYPE_BINARY: cp = "<binary>"; break;
450 case AV_OPT_TYPE_IMAGE_SIZE: cp = "<image_size>"; break;
451 case AV_OPT_TYPE_VIDEO_RATE: cp = "<video_rate>"; break;
452 case AV_OPT_TYPE_PIXEL_FMT: cp = "<pix_fmt>"; break;
453 case AV_OPT_TYPE_SAMPLE_FMT: cp = "<sample_fmt>"; break;
454 case AV_OPT_TYPE_DURATION: cp = "<duration>"; break;
455 case AV_OPT_TYPE_COLOR: cp = "<color>"; break;
456 case AV_OPT_TYPE_CHLAYOUT: cp = "<channel_layout>"; break;
457 default: cp = "<undef>"; break;
459 return sprintf(rp, "%s", cp);
461 int PluginFClient_Opt::scalar(double d, char *rp)
464 if( d == INT_MAX ) cp = "INT_MAX";
465 else if( d == INT_MIN ) cp = "INT_MIN";
466 else if( d == UINT32_MAX ) cp = "UINT32_MAX";
467 else if( d == (double)INT64_MAX ) cp = "I64_MAX";
468 else if( d == INT64_MIN ) cp = "I64_MIN";
469 else if( d == FLT_MAX ) cp = "FLT_MAX";
470 else if( d == FLT_MIN ) cp = "FLT_MIN";
471 else if( d == -FLT_MAX ) cp = "-FLT_MAX";
472 else if( d == -FLT_MIN ) cp = "-FLT_MIN";
473 else if( d == DBL_MAX ) cp = "DBL_MAX";
474 else if( d == DBL_MIN ) cp = "DBL_MIN";
475 else if( d == -DBL_MAX ) cp = "-DBL_MAX";
476 else if( d == -DBL_MIN ) cp = "-DBL_MIN";
477 else if( d == 0 ) cp = signbit(d) ? "-0" : "0";
478 else if( isnan(d) ) cp = signbit(d) ? "-NAN" : "NAN";
479 else if( isinf(d) ) cp = signbit(d) ? "-INF" : "INF";
480 else return sprintf(rp, "%g", d);
481 return sprintf(rp, "%s", cp);
484 int PluginFClient_Opt::ranges(char *rp)
486 const AVClass *filt_class = filter_class();
487 if( !filt_class || !filt_class->option ) return 0;
489 case AV_OPT_TYPE_INT:
490 case AV_OPT_TYPE_INT64:
491 case AV_OPT_TYPE_DOUBLE:
492 case AV_OPT_TYPE_FLOAT: break;
495 AVOptionRanges *r = 0;
496 void *obj = &filt_class;
498 if( av_opt_query_ranges(&r, obj, opt->name, AV_OPT_SEARCH_FAKE_OBJ) < 0 ) return 0;
499 for( int i=0; i<r->nb_ranges; ++i ) {
500 cp += sprintf(cp, " ("); cp += scalar(r->range[i]->value_min, cp);
501 cp += sprintf(cp, ".."); cp += scalar(r->range[i]->value_max, cp);
502 cp += sprintf(cp, ")");
504 av_opt_freep_ranges(&r);
508 int PluginFClient_Opt::units(ArrayList<const AVOption *> &opts)
510 if( !this->opt->unit ) return 0;
511 const AVClass *filt_class = filter_class();
512 if( !filt_class || !filt_class->option ) return 0;
513 void *obj = &filt_class;
514 const AVOption *opt = NULL;
515 while( (opt=av_opt_next(obj, opt)) != 0 ) {
516 if( !opt->unit ) continue;
517 if( opt->type != AV_OPT_TYPE_CONST ) continue;
518 if( strcmp(this->opt->unit, opt->unit) ) continue;
521 if( opts[i]->default_val.i64 != opt->default_val.i64 ) continue;
522 if( strlen(opts[i]->name) < strlen(opt->name) ) opts[i] = opt;
531 int PluginFClient_Opt::units(char *rp)
533 ArrayList<const AVOption *> opts;
537 cp += sprintf(cp, " [%s:", this->opt->unit);
538 for( int i=0; i<n; ++i )
539 cp += sprintf(cp, " %s", opts[i]->name);
540 cp += sprintf(cp, "]:");
544 const char *PluginFClient_Opt::tip()
549 int PluginFClient_OptPanel::update()
551 const char *cols[] = { "option", "value", };
552 const int col1_w = xS(150);
553 int wids[] = { col1_w, get_w()-col1_w };
554 BC_ListBox::update(&items[0], &cols[0], &wids[0], sizeof(items)/sizeof(items[0]),
555 get_xposition(), get_yposition(), get_highlighted_item());
560 PluginFClientWindow::PluginFClientWindow(PluginFClient *ffmpeg)
561 : PluginClientWindow(ffmpeg->plugin, xS(600), yS(300), xS(600), yS(300), 1)
563 this->ffmpeg = ffmpeg;
567 PluginFClientWindow::~PluginFClientWindow()
571 void PluginFClientWindow::create_objects()
573 int xs8 = xS(8), xs10 = xS(10), ys10 = yS(10);
574 char string[BCTEXTLEN];
576 int x = xs10, y = ys10;
577 const char *descr = ffmpeg->config.ffilt->description();
578 if( !descr ) descr = ffmpeg->config.ffilt->filter_name();
579 add_subwindow(title = new BC_Title(x, y, descr));
580 y += title->get_h() + ys10;
582 sprintf(string, _("Type: "));
583 add_subwindow(title = new BC_Title(x0, y, string));
584 x0 += title->get_w() + xs8;
585 add_subwindow(type = new BC_Title(x0, y, (char *)""));
587 sprintf(string, _("Range: "));
588 add_subwindow(title = new BC_Title(x0, y, string));
589 x0 += title->get_w() + xs8;
590 add_subwindow(range = new BC_Title(x0, y, (char *)""));
591 int x1 = get_w() - BC_GenericButton::calculate_w(this, _("Reset")) - xs8;
592 add_subwindow(reset = new PluginFClientReset(this, x1, y));
593 y += title->get_h() + ys10;
595 add_subwindow(units = new PluginFClientUnits(this, x0, y, xS(120)));
596 x0 += units->get_w() + xs8;
597 x1 = get_w() - BC_GenericButton::calculate_w(this, _("Apply")) - xs8;
598 add_subwindow(apply = new PluginFClientApply(this, x1, y));
599 add_subwindow(text = new PluginFClientText(this, x0, y, x1-x0 - xs8));
600 y += title->get_h() + ys10;
601 add_subwindow(pot = new PluginFClientPot(this, x, y));
602 x1 = x + pot->get_w() + xs10;
603 add_subwindow(slider = new PluginFClientSlider(this, x1, y+ys10));
604 y += pot->get_h() + ys10;
606 panel_x = x; panel_y = y;
607 panel_w = get_w()-xs10 - panel_x;
608 panel_h = get_h()-ys10 - panel_y;
609 panel = new PluginFClient_OptPanel(this, panel_x, panel_y, panel_w, panel_h);
610 add_subwindow(panel);
611 panel->create_objects();
612 ffmpeg->config.update();
617 void PluginFClientWindow::draw()
622 int PluginFClientWindow::resize_event(int w, int h)
624 int xs8 = xS(8), xs10 = xS(10), ys10 = yS(10);
625 int x = get_w() - BC_GenericButton::calculate_w(this, _("Reset")) - xs8;
626 int y = reset->get_y();
627 reset->reposition_window(x, y);
628 int x1 = get_w() - BC_GenericButton::calculate_w(this, _("Apply")) - xs8;
629 int y1 = units->get_y();
630 apply->reposition_window(x1, y1);
631 int x0 = units->get_x() + units->get_w() + xs8;
632 int y0 = units->get_y();
633 text->reposition_window(x0,y0, x1-x0-xs8);
634 x1 = pot->get_x() + pot->get_w() + xs10;
635 int w1 = w - slider->get_x() - xS(20);
636 slider->set_pointer_motion_range(w1);
637 slider->reposition_window(x1, slider->get_y(), w1, slider->get_h());
638 panel_w = get_w()-xs10 - panel_x;
639 panel_h = get_h()-ys10 - panel_y;
640 panel->reposition_window(panel_x,panel_y, panel_w, panel_h);
645 PluginFClient::PluginFClient(PluginClient *plugin, const char *name)
647 this->plugin = plugin;
651 plugin_position = -1;
652 filter_position = -1;
654 sprintf(title, "F_%s", name);
655 config.initialize(name);
656 curr_config.initialize(name);
659 PluginFClient::~PluginFClient()
664 bool PluginFClient::is_audio(const AVFilter *fp)
666 if( !fp->outputs ) return 0;
667 #if LIBAVFILTER_VERSION_MAJOR > 8
668 if( avfilter_filter_pad_count(fp, 1) > 1 ) return 0;
670 if( avfilter_pad_count(fp->outputs) > 1 ) return 0;
672 if( !avfilter_pad_get_name(fp->outputs, 0) ) return 0;
673 if( avfilter_pad_get_type(fp->outputs, 0) != AVMEDIA_TYPE_AUDIO ) return 0;
674 if( !fp->inputs ) return 1;
675 #if LIBAVFILTER_VERSION_MAJOR > 8
676 if( avfilter_filter_pad_count(fp, 0) > 1 ) return 0;
678 if( avfilter_pad_count(fp->inputs) > 1 ) return 0;
680 if( !avfilter_pad_get_name(fp->inputs, 0) ) return 0;
681 if( avfilter_pad_get_type(fp->inputs, 0) != AVMEDIA_TYPE_AUDIO ) return 0;
684 bool PluginFClient::is_video(const AVFilter *fp)
686 if( !fp->outputs ) return 0;
687 #if LIBAVFILTER_VERSION_MAJOR > 8
688 if( avfilter_filter_pad_count(fp, 1) > 1 ) return 0;
690 if( avfilter_pad_count(fp->outputs) > 1 ) return 0;
692 if( !avfilter_pad_get_name(fp->outputs, 0) ) return 0;
693 if( avfilter_pad_get_type(fp->outputs, 0) != AVMEDIA_TYPE_VIDEO ) return 0;
694 if( !fp->inputs ) return 1;
695 #if LIBAVFILTER_VERSION_MAJOR > 8
696 if( avfilter_filter_pad_count(fp, 0) > 1 ) return 0;
698 if( avfilter_pad_count(fp->inputs) > 1 ) return 0;
700 if( !avfilter_pad_get_name(fp->inputs, 0) ) return 0;
701 if( avfilter_pad_get_type(fp->inputs, 0) != AVMEDIA_TYPE_VIDEO ) return 0;
705 NEW_WINDOW_MACRO(PluginFClient, PluginFClientWindow)
707 int PluginFClient::load_configuration()
709 int64_t src_position = get_source_position();
710 KeyFrame *prev_keyframe = get_prev_keyframe(src_position);
711 config.copy_from(curr_config);
712 read_data(prev_keyframe);
713 int ret = !config.equivalent(curr_config) ? 1 : 0;
718 void PluginFClient::render_gui(void *data, int size)
720 PluginFClientConfig *conf = (PluginFClientConfig *)data;
721 config.copy_from(*conf);
722 KeyFrame *keyframe = plugin->server->get_keyframe();
727 void PluginFClient::update_gui()
729 PluginClientThread *thread = plugin->get_thread();
730 if( !thread ) return;
731 PluginFClientWindow *window = (PluginFClientWindow*)thread->get_window();
732 window->lock_window("PluginFClient::update_gui");
733 load_configuration();
734 if( config.update() > 0 )
736 window->unlock_window();
739 const char *PluginFClient::plugin_title()
744 char *PluginFClient::to_upper(char *cp, const char *sp)
747 while( *sp != 0 ) *bp++ = toupper(*sp++);
752 void PluginFClient::save_data(KeyFrame *keyframe)
755 char string[BCTEXTLEN];
757 // cause data to be stored directly in text
758 output.set_shared_output(keyframe->xbuf);
759 output.tag.set_title(to_upper(string, plugin_title()));
760 const AVClass *filt_class = config.filter_class();
761 if( filt_class && filt_class->option ) {
762 void *obj = config.filter_config();
763 const AVOption *opt = NULL;
764 while( (opt=av_opt_next(obj, opt)) != 0 ) {
766 if( av_opt_get(obj, opt->name, 0, &buf) < 0 ) continue;
767 output.tag.set_property(opt->name, (const char *)buf);
771 if( (config.ffilt->filter->flags & AVFILTER_FLAG_SLICE_THREADS) != 0 ) {
773 if( av_opt_get(config.ffilt->fctx, "threads", 0, &buf) >= 0 && buf ) {
774 output.tag.set_property("threads", (const char *)buf);
780 output.terminate_string();
783 void PluginFClient::read_data(KeyFrame *keyframe)
786 char string[BCTEXTLEN];
787 input.set_shared_input(keyframe->xbuf);
789 while( !input.read_tag() ) {
790 to_upper(string, plugin_title());
791 if( !input.tag.title_is(string) ) continue;
792 const AVClass *filt_class = config.filter_class();
793 if( filt_class && filt_class->option ) {
794 void *obj = config.filter_config();
795 const AVOption *opt = NULL;
796 while( (opt=av_opt_next(obj, opt)) != 0 ) {
797 const char *v = input.tag.get_property(opt->name);
798 if( v ) av_opt_set(obj, opt->name, v, 0);
800 if( (config.ffilt->filter->flags & AVFILTER_FLAG_SLICE_THREADS) != 0 ) {
801 const char *v = input.tag.get_property("threads");
802 if( v ) av_opt_set(config.ffilt->fctx, "threads", v, 0);
808 int PluginFClient::activate()
811 avfilter_link(fsrc, 0, ffilt->fctx, 0);
812 avfilter_link(ffilt->fctx, 0, fsink, 0);
813 int ret = avfilter_graph_config(ffilt->graph, NULL);
815 curr_config.copy_from(config);
821 void PluginFClient::reactivate()
823 delete ffilt; ffilt = 0;
827 PluginFAClient::PluginFAClient(PluginServer *server, const char *name)
828 : PluginAClient(server), PluginFClient(this, name)
832 PluginFAClient::~PluginFAClient()
836 int PluginFAClient::activate()
838 if( activated ) return activated;
839 ffilt = PluginFFilter::new_ffilter(name, &config);
841 config.copy_from(curr_config);
842 send_configure_change();
843 send_render_gui(&config, sizeof(config));
844 ffilt = PluginFFilter::new_ffilter(name, &config);
846 AVSampleFormat sample_fmt = AV_SAMPLE_FMT_FLTP;
847 int channels = PluginClient::total_in_buffers;
848 //uint64_t layout = (((uint64_t)1)<<channels) - 1;
849 AVChannelLayout layout;
851 av_channel_layout_default(&layout, channels);
853 char chLayoutDescription[128];
854 av_channel_layout_describe(&layout, chLayoutDescription, sizeof(chLayoutDescription));
856 AVFilterGraph *graph = !ffilt ? 0 : ffilt->graph;
857 int ret = !graph ? -1 : 0;
858 if( ret >= 0 && ffilt->filter->inputs ) {
859 char args[BCTEXTLEN];
860 snprintf(args, sizeof(args),
861 "time_base=%d/%d:sample_rate=%d:sample_fmt=%s:channel_layout=%s:channels=%i",
862 1, sample_rate, sample_rate, av_get_sample_fmt_name(sample_fmt), chLayoutDescription, channels);
863 ret = avfilter_graph_create_filter(&fsrc, avfilter_get_by_name("abuffer"),
864 "in", args, NULL, graph);
865 if(ret <0) printf("abuffer failed!\n");
868 ret = avfilter_graph_create_filter(&fsink, avfilter_get_by_name("abuffersink"),
869 "out", NULL, NULL, graph);
871 ret = av_opt_set_bin(fsink, "sample_fmts",
872 (uint8_t*)&sample_fmt, sizeof(sample_fmt), AV_OPT_SEARCH_CHILDREN);
874 ret = av_opt_set(fsink, "ch_layouts",
875 chLayoutDescription, AV_OPT_SEARCH_CHILDREN);
877 ret = av_opt_set_bin(fsink, "sample_rates",
878 (uint8_t*)&sample_rate, sizeof(sample_rate), AV_OPT_SEARCH_CHILDREN);
880 ret = PluginFClient::activate();
881 if( ret < 0 && activated >= 0 ) {
882 ff_err(ret, "PluginFAClient::activate: %s failed\n", plugin_title());
889 static AVRational best_frame_rate(double frame_rate)
891 static const int m1 = 1001*12, m2 = 1000*12;
892 static const int freqs[] = {
893 40*m1, 48*m1, 50*m1, 60*m1, 80*m1,120*m1, 240*m1,
894 24*m2, 30*m2, 60*m2, 12*m2, 15*m2, 48*m2, 90*m2,
895 100*m2, 120*m2, 144*m2, 72*m2, 0,
898 int freq, best_freq = 0;
899 for( int i=0; (freq = i<30*12 ? (i+1)*1001 : freqs[i-30*12]); ++i ) {
900 double framerate = (double)freq / m1;
901 double err = fabs(frame_rate/framerate - 1.);
902 if( err >= max_err ) continue;
906 return max_err < 0.0001 ?
907 (AVRational) { best_freq, m1 } :
908 (AVRational) { 0, 0 };
911 int PluginFVClient::activate(int width, int height, int color_model)
913 if( activated ) return activated;
914 ffilt = PluginFFilter::new_ffilter(name, &config);
916 config.copy_from(curr_config);
917 send_configure_change();
918 send_render_gui(&config, sizeof(config));
919 ffilt = PluginFFilter::new_ffilter(name, &config);
921 AVPixelFormat pix_fmt = color_model_to_pix_fmt(color_model);
922 AVFilterGraph *graph = !ffilt ? 0 : ffilt->graph;
923 int ret = !graph ? -1 : 0;
924 if( ret >= 0 && ffilt->filter->inputs ) {
925 curr_config.copy_from(config);
926 if( pix_fmt == AV_PIX_FMT_NB ) {
927 int bpp = BC_CModels::calculate_pixelsize(color_model) * 8;
928 int bits_per_comp = bpp / BC_CModels::components(color_model);
929 int alpha = BC_CModels::has_alpha(color_model);
930 pix_fmt = bits_per_comp > 8 ?
931 !alpha ? AV_PIX_FMT_RGB48LE : AV_PIX_FMT_RGBA64LE :
932 !alpha ? AV_PIX_FMT_RGB24 : AV_PIX_FMT_RGBA ;
934 int aspect_w = 1, aspect_h = 1; // XXX
935 AVRational best_rate = best_frame_rate(frame_rate);
936 char args[BCTEXTLEN];
937 snprintf(args, sizeof(args),
938 "video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d",
939 width, height, pix_fmt, best_rate.den, best_rate.num, aspect_w, aspect_h);
940 ret = avfilter_graph_create_filter(&fsrc, avfilter_get_by_name("buffer"),
941 "in", args, NULL, graph);
944 ret = avfilter_graph_create_filter(&fsink, avfilter_get_by_name("buffersink"),
945 "out", NULL, NULL, graph);
947 ret = av_opt_set_bin(fsink, "pix_fmts",
948 (uint8_t*)&pix_fmt, sizeof(pix_fmt), AV_OPT_SEARCH_CHILDREN);
950 ret = PluginFClient::activate();
951 if( ret < 0 && activated >= 0 ) {
952 ff_err(ret, "PluginFVClient::activate() %s\n", plugin_title());
958 int PluginFAClient::get_inchannels()
960 AVFilterContext *fctx = ffilt->fctx;
961 AVFilterLink **links = !fctx->nb_inputs ? 0 : fctx->inputs;
962 #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(59,24,100)
963 return !links ? 0 : links[0]->ch_layout.nb_channels;
965 return !links ? 0 : links[0]->channels;
969 int PluginFAClient::get_outchannels()
971 AVFilterContext *fctx = ffilt->fctx;
972 AVFilterLink **links = !fctx->nb_outputs ? 0 : fctx->outputs;
973 #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(59,24,100)
974 return !links ? 0 : links[0]->ch_layout.nb_channels;
976 return !links ? 0 : links[0]->channels;
980 int PluginFAClient::process_buffer(int64_t size, Samples **buffer, int64_t start_position, int sample_rate)
982 int total_in = PluginClient::total_in_buffers;
983 int total_out = PluginClient::total_out_buffers;
984 int in_channels = 0, out_channels = 0;
986 if( load_configuration() )
989 if( plugin_position != start_position )
990 filter_position = plugin_position = start_position;
993 int ret = activate();
994 if( ret >= 0 && !(frame = av_frame_alloc()) ) {
995 fprintf(stderr, "PluginFAClient::process_buffer: av_frame_alloc failed\n");
996 ret = AVERROR(ENOMEM);
999 in_channels = get_inchannels();
1000 out_channels = get_outchannels();
1001 #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(59,24,100)
1002 AVChannelLayout in_ch_layout;
1003 AVFilterContext *fctx = ffilt->fctx;
1004 AVFilterLink **links = !fctx->nb_outputs ? 0 : fctx->outputs;
1005 av_channel_layout_copy(&in_ch_layout, &links[0]->ch_layout);
1007 frame->nb_samples = size;
1008 frame->format = AV_SAMPLE_FMT_FLTP;
1009 #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(61,3,100)
1010 av_channel_layout_copy(&frame->ch_layout, &in_ch_layout);
1012 frame->channel_layout = (1<<in_channels)-1;
1014 frame->sample_rate = sample_rate;
1015 frame->pts = filter_position;
1019 while( ret >= 0 && --retry >= 0 ) {
1020 ret = av_buffersink_get_frame(fsink, frame);
1021 if( ret >= 0 || ret != AVERROR(EAGAIN) ) break;
1022 if( !fsrc ) { ret = AVERROR(EIO); break; }
1023 for( int i=0; i<total_in; ++i )
1024 read_samples(buffer[i], i, sample_rate, filter_position, size);
1025 filter_position += size;
1026 ret = av_frame_get_buffer(frame, 0);
1027 if( ret < 0 ) break;
1028 float **in_buffers = (float **)&frame->extended_data[0];
1029 for(int i = 0; i < in_channels; i++) {
1030 float *in_buffer = in_buffers[i];
1031 int k = i < total_in ? i : 0;
1032 double *in_ptr = buffer[k]->get_data();
1033 for(int j = 0; j < size; j++) in_buffer[j] = in_ptr[j];
1035 ret = av_buffersrc_add_frame_flags(fsrc, frame, 0);
1037 if( ret >= 0 && retry < 0 )
1038 ret = AVERROR(EAGAIN);
1041 int nbfrs = total_out;
1042 if( out_channels < nbfrs ) nbfrs = out_channels;
1043 float **out_buffers = (float **)&frame->extended_data[0];
1045 while( i < nbfrs ) {
1046 float *out_buffer = out_buffers[i];
1047 double *out_ptr = buffer[i++]->get_data();
1048 for(int j = 0; j < size; j++) out_ptr[j] = out_buffer[j];
1050 while( i < total_out ) {
1051 double *out_ptr = buffer[i++]->get_data();
1052 bzero(out_ptr, sizeof(double) * size);
1056 int64_t position = PluginFClient::get_source_position();
1057 double t0 = (double)position/sample_rate, dt = 1./sample_rate;
1058 for( int i=0; i<total_out; ++i ) {
1059 double t = t0, *out = buffer[i]->get_data();
1060 for( int k=size; --k>=0; t+=dt ) {
1061 double w = int(2*t) & 1 ? 2*M_PI*440 : 2*M_PI*697;
1062 *out++ = sin(t * w);
1065 ff_err(ret, "PluginFAClient::process_buffer() %s\n", plugin_title());
1068 av_frame_free(&frame);
1069 plugin_position += size;
1074 PluginFVClient::PluginFVClient(PluginServer *server, const char *name)
1075 : PluginVClient(server),
1076 PluginFClient(this, name),
1077 FFVideoConvert(server->preferences)
1081 PluginFVClient::~PluginFVClient()
1085 int PluginFVClient::process_buffer(VFrame **frames, int64_t position, double frame_rate)
1087 VFrame *vframe = *frames;
1088 int width = vframe->get_w();
1089 int height = vframe->get_h();
1091 if( load_configuration() )
1094 if( plugin_position != position )
1095 filter_position = plugin_position = position;
1097 int colormodel = vframe->get_color_model();
1098 int ret = activate(width, height, colormodel);
1099 AVPixelFormat pix_fmt = fsrc ?
1100 (AVPixelFormat) fsrc->outputs[0]->format :
1101 color_model_to_pix_fmt(colormodel);
1102 if( pix_fmt <= AV_PIX_FMT_NONE || pix_fmt >= AV_PIX_FMT_NB )
1103 pix_fmt = AV_PIX_FMT_RGBA;
1106 if( ret >= 0 && !(frame = av_frame_alloc()) ) {
1107 fprintf(stderr, "PluginFVClient::process_buffer: av_frame_alloc failed\n");
1108 ret = AVERROR(ENOMEM);
1112 while( ret >= 0 && --retry >= 0 ) {
1113 ret = av_buffersink_get_frame(fsink, frame);
1114 if( ret >= 0 || ret != AVERROR(EAGAIN) ) break;
1115 if( !fsrc ) { ret = AVERROR(EIO); break; }
1116 read_frame(vframe, 0, filter_position, frame_rate, 0);
1117 frame->format = pix_fmt;
1118 frame->width = width;
1119 frame->height = height;
1120 frame->pts = filter_position++;
1121 ret = av_frame_get_buffer(frame, 32);
1122 if( ret < 0 ) break;
1123 ret = transfer_pixfmt(vframe, frame);
1124 if( ret < 0 ) break;
1125 ret = av_buffersrc_add_frame_flags(fsrc, frame, 0);
1127 if( ret >= 0 && retry < 0 )
1128 ret = AVERROR(EAGAIN);
1131 pix_fmt = (AVPixelFormat) frame->format;
1132 ret = transfer_cmodel(vframe, frame);
1135 ff_err(ret, "PluginFVClient::process_buffer() %s\n", plugin_title());
1136 if( position & 1 ) vframe->clear_frame();
1140 av_frame_free(&frame);
1141 return ret >= 0 ? 0 : 1;
1145 PluginFClient_OptName:: PluginFClient_OptName(PluginFClient_Opt *opt)
1148 set_text(opt->opt->name);
1151 PluginFClient_OptValue::PluginFClient_OptValue(PluginFClient_Opt *opt)
1157 void PluginFClient_OptValue::update()
1159 char val[BCTEXTLEN]; val[0] = 0;
1160 opt->get(val, sizeof(val));
1165 PluginFClient_Opt::PluginFClient_Opt(PluginFClientConfig *conf, const AVOption *opt)
1169 item_name = new PluginFClient_OptName(this);
1170 item_value = new PluginFClient_OptValue(this);
1173 PluginFClient_Opt::~PluginFClient_Opt()
1179 const char *PluginFClientConfig::get(const char *name)
1182 if( av_opt_get(filter_config(), name, 0, &bp) >= 0 ||
1183 av_opt_get(ffilt->fctx, name, 0, &bp) >= 0 )
1184 return (const char *)bp;
1187 char *PluginFClient_Opt::get(char *vp, int sz)
1189 const char *val = conf->get(opt->name);
1191 strncpy(vp, val, sz);
1200 void PluginFClientConfig::set(const char *name, const char *val)
1202 if( av_opt_set(filter_config(), name, val, 0) < 0 )
1203 av_opt_set(ffilt->fctx, name, val, 0);
1205 void PluginFClient_Opt::set(const char *val)
1207 conf->set(opt->name, val);
1210 void PluginFFilter::uninit()
1214 static int get_defaults(const char *name, char *args)
1217 char defaults_path[BCTEXTLEN];
1218 FFMPEG::set_option_path(defaults_path, "plugin.opts");
1219 FILE *fp = fopen(defaults_path,"r");
1221 char ff_plugin[BCSTRLEN], ff_args[BCTEXTLEN], *ap = 0;
1222 while( fgets(ff_args, sizeof(ff_args), fp) ) {
1224 if( *cp == ';' ) continue;
1225 if( *cp == '#' ) ++cp;
1226 char *bp = ff_plugin, *ep = bp + sizeof(ff_plugin)-1;
1227 while( bp < ep && *cp && *cp != '\n' && *cp != ' ' ) *bp++ = *cp++;
1229 if( !strcmp(ff_plugin, name) ) { ap = cp; break; }
1233 if( ff_args[0] == '#' ) return -1;
1234 while( *ap == ' ' ) ++ap;
1235 while( *ap && *ap != '\n' ) *args++ = *ap++;
1240 bool PluginFFilter::is_audio()
1242 return PluginFClient::is_audio(filter);
1245 bool PluginFFilter::is_video()
1247 return PluginFClient::is_video(filter);
1250 int PluginFFilter::init(const char *name, PluginFClientConfig *conf)
1252 char args[BCTEXTLEN];
1253 int ret = get_defaults(name, args);
1254 if( ret < 0 ) return 0;
1255 PluginFLogLevel errs(AV_LOG_ERROR);
1256 this->filter = avfilter_get_by_name(name);
1257 if( !this->filter ) return AVERROR(ENOENT);
1258 int flag_mask = AVFILTER_FLAG_DYNAMIC_INPUTS | AVFILTER_FLAG_DYNAMIC_OUTPUTS;
1259 if( filter->flags & flag_mask ) return AVERROR(EPERM);
1260 if( !this->is_audio() && !this->is_video() ) return AVERROR(EIO);
1261 this->graph = avfilter_graph_alloc();
1262 if( !this->graph ) return AVERROR(ENOMEM);
1263 static int inst = 0;
1264 char inst_name[BCSTRLEN];
1265 sprintf(inst_name,"%s_%d", name, ++inst);
1266 if( conf && (filter->flags & AVFILTER_FLAG_SLICE_THREADS) != 0 ) {
1267 graph->thread_type = AVFILTER_THREAD_SLICE;
1268 graph->nb_threads = atoi(conf->get("threads"));
1271 graph->thread_type = 0;
1272 graph->nb_threads = 0;
1274 fctx = avfilter_graph_alloc_filter(graph, filter, inst_name);
1275 if( !fctx ) return AVERROR(ENOMEM);
1276 fctx->thread_type = graph->thread_type; // bug in avfilter
1278 AVDictionary *opts = 0;
1279 for( int i=0; i<conf->size(); ++i ) {
1280 PluginFClient_Opt *op = conf->get(i);
1281 const char *name = op->opt->name;
1282 char val[BCTEXTLEN], *vp = op->get(val, sizeof(val));
1285 // unspecified opts cause a special behavior in some filters (lut3d)
1286 // so... if opt value is the default, skip it or no special behavior
1287 if( av_opt_get(filter_config(), name, 0, &bp) >= 0 ) {
1288 int result = strcmp((const char *)bp, vp);
1290 if( !result ) continue;
1292 av_dict_set(&opts, name, vp, 0);
1294 ret = avfilter_init_dict(fctx, &opts);
1295 av_dict_free(&opts);
1298 ret = avfilter_init_str(fctx, args);
1299 return ret < 0 ? ret : 1;
1303 PluginFFilter::PluginFFilter()
1310 PluginFFilter::~PluginFFilter()
1312 PluginFLogLevel errs(AV_LOG_ERROR);
1313 avfilter_graph_free(&graph);
1314 filter = 0; fctx = 0;
1317 PluginFFilter *PluginFFilter::new_ffilter(const char *name, PluginFClientConfig *conf)
1319 PluginFFilter *ffilt = new PluginFFilter;
1320 int ret = ffilt->init(name, conf);
1321 if( ret < 0 ) ff_err(ret, "PluginFFilter::new_ffilter(%s)\n", name);
1322 if( ret <= 0 ) { delete ffilt; ffilt = 0; }
1326 PluginClient *PluginServer::new_ffmpeg_plugin()
1328 const AVFilter *filter = avfilter_get_by_name(ff_name);
1329 if( !filter ) return 0;
1330 PluginFClient *ffmpeg =
1331 PluginFClient::is_audio(filter) ?
1332 (PluginFClient *)new PluginFAClient(this, ff_name) :
1333 PluginFClient::is_video(filter) ?
1334 (PluginFClient *)new PluginFVClient(this, ff_name) :
1336 if( !ffmpeg ) return 0;
1337 return ffmpeg->plugin;
1341 PluginServer* MWindow::new_ffmpeg_server(MWindow *mwindow, const char *name)
1343 PluginFFilter *ffilt = PluginFFilter::new_ffilter(name);
1344 if( !ffilt ) return 0;
1346 return new PluginServer(mwindow, name, PLUGIN_TYPE_FFMPEG);
1349 void MWindow::init_ffmpeg_index(MWindow *mwindow, Preferences *preferences, FILE *fp)
1351 PluginFLogLevel errs(AV_LOG_ERROR);
1352 const AVFilter *filter = 0; void *idx = 0;
1353 while( (filter=av_filter_iterate(&idx)) != 0 ) {
1354 //printf("%s\n",filter->name);
1355 PluginServer *server = new_ffmpeg_server(mwindow, filter->name);
1357 int result = server->open_plugin(1, preferences, 0, 0);
1359 server->write_table(fp, filter->name, PLUGIN_FFMPEG_ID, 0);
1360 server->close_plugin();
1363 if( result ) fprintf(fp, "#%s\n", filter->name);
1368 void MWindow::init_ffmpeg()