8 #include "bcwindowbase.h"
13 #include "pluginfclient.h"
14 #include "pluginserver.h"
20 static void ff_err(int ret, const char *fmt, ...)
25 vsnprintf(msg, sizeof(msg), fmt, ap);
27 char errmsg[BCSTRLEN];
28 av_strerror(ret, errmsg, sizeof(errmsg));
29 fprintf(stderr,_("%s err: %s\n"),msg, errmsg);
32 PluginFClientConfig::PluginFClientConfig()
37 PluginFClientConfig::~PluginFClientConfig()
43 int PluginFClientConfig::equivalent(PluginFClientConfig &that)
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;
56 void PluginFClientConfig::copy_from(PluginFClientConfig &that)
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;
63 while( --k >= 0 && strcmp(nm, conf[k]->opt->name) );
65 PluginFClient_Opt *fopt = conf[k];
66 char str[BCTEXTLEN], *sp = vp->get(str, sizeof(str));
67 if( sp ) fopt->set(sp);
71 void PluginFClientConfig::interpolate(PluginFClientConfig &prev, PluginFClientConfig &next,
72 int64_t prev_frame, int64_t next_frame, int64_t current_frame)
77 void PluginFClientConfig::initialize(const char *name)
79 ffilt = PluginFFilter::new_ffilter(name);
80 const AVOption *opt = 0;
81 void *obj = ffilt->filter_config();
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;
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;
95 if( strlen(op->name) < strlen(opt->name) )
99 PluginFClient_Opt *fopt = new PluginFClient_Opt(this, opt);
105 int PluginFClientConfig::update()
108 PluginFClientConfig &conf = *this;
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();
120 void PluginFClientConfig::dump(FILE *fp)
122 const AVOption *opt = 0;
123 const AVClass *obj = filter_class();
124 PluginFClientConfig &conf = *this;
126 while( (opt=av_opt_next(&obj, opt)) != 0 ) {
127 if( opt->type == AV_OPT_TYPE_CONST ) continue;
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);
135 char unt[BCTEXTLEN], *up = unt;
137 fprintf(fp, "%s", unt);
144 PluginFClientReset(PluginFClientWindow *fwin, int x, int y)
145 : BC_GenericButton(x, y, _("Reset"))
151 ~PluginFClientReset()
155 int PluginFClientReset::handle_event()
157 av_opt_set_defaults(fwin->ffmpeg->config.filter_config());
158 if( fwin->ffmpeg->config.update() > 0 )
160 fwin->ffmpeg->plugin->send_configure_change();
165 PluginFClientText(PluginFClientWindow *fwin, int x, int y, int w)
166 : BC_TextBox(x, y, w, 1, (char *)"")
176 int PluginFClientText::handle_event()
182 PluginFClientUnits(PluginFClientWindow *fwin, int x, int y, int w)
183 : BC_PopupMenu(x, y, w, "")
189 ~PluginFClientUnits()
193 int PluginFClientUnits::handle_event()
195 const char *text = get_text();
196 if( text && fwin->selected ) {
197 if( text ) fwin->selected->set(text);
198 fwin->selected->item_value->update();
200 fwin->ffmpeg->plugin->send_configure_change();
206 PluginFClientApply(PluginFClientWindow *fwin, int x, int y)
207 : BC_GenericButton(x, y, _("Apply"))
213 ~PluginFClientApply()
217 int PluginFClientApply::handle_event()
219 const char *text = fwin->text->get_text();
220 if( text && fwin->selected ) {
221 fwin->selected->set(text);
222 fwin->selected->item_value->update();
224 fwin->ffmpeg->plugin->send_configure_change();
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])
235 update(); // init col/wid/columns
238 PluginFClient_OptPanel::
239 ~PluginFClient_OptPanel()
243 void PluginFClient_OptPanel::create_objects()
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);
253 int PluginFClient_OptPanel::cursor_leave_event()
259 void PluginFClientWindow::update(PluginFClient_Opt *opt)
261 if( selected != opt ) {
262 if( selected ) selected->item_name->set_selected(0);
264 if( selected ) selected->item_name->set_selected(1);
266 clear_box(0,0, 0,panel->get_y());
267 char str[BCTEXTLEN], *sp;
269 if( opt ) opt->types(sp);
272 if( opt ) opt->ranges(sp);
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));
290 int PluginFClient_OptPanel::selection_changed()
292 PluginFClient_Opt *opt = 0;
293 BC_ListBoxItem *item = get_selection(0, 0);
295 PluginFClient_OptName *opt_name = (PluginFClient_OptName *)item;
299 fwin->panel->set_tooltip(!opt ? 0 : opt->tip());
300 fwin->panel->show_tooltip();
304 void *PluginFClient_Opt::filter_config()
306 return conf->filter_config();
308 const AVClass *PluginFClient_Opt::filter_class()
310 return conf->filter_class();
313 int PluginFClient_Opt::types(char *rp)
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;
334 return sprintf(rp, "%s", cp);
336 int PluginFClient_Opt::scalar(double d, char *rp)
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);
359 int PluginFClient_Opt::ranges(char *rp)
361 const AVClass *filt_class = filter_class();
362 if( !filt_class || !filt_class->option ) return 0;
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;
370 AVOptionRanges *r = 0;
371 void *obj = &filt_class;
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, ")");
379 av_opt_freep_ranges(&r);
383 int PluginFClient_Opt::units(ArrayList<const AVOption *> &opts)
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;
396 if( opts[i]->default_val.i64 != opt->default_val.i64 ) continue;
397 if( strlen(opts[i]->name) < strlen(opt->name) ) opts[i] = opt;
406 int PluginFClient_Opt::units(char *rp)
408 ArrayList<const AVOption *> opts;
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, "]:");
419 const char *PluginFClient_Opt::tip()
424 int PluginFClient_OptPanel::update()
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]));
434 PluginFClientWindow::PluginFClientWindow(PluginFClient *ffmpeg)
435 : PluginClientWindow(ffmpeg->plugin, 600, 300, 600, 300, 1)
437 this->ffmpeg = ffmpeg;
441 PluginFClientWindow::~PluginFClientWindow()
445 void PluginFClientWindow::create_objects()
447 char string[BCTEXTLEN];
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;
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 *)""));
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;
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;
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();
486 void PluginFClientWindow::draw()
491 int PluginFClientWindow::resize_event(int w, int h)
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);
508 PluginFClient::PluginFClient(PluginClient *plugin, const char *name)
510 this->plugin = plugin;
514 plugin_position = -1;
515 filter_position = -1;
517 sprintf(title, "F_%s", name);
518 config.initialize(name);
519 curr_config.initialize(name);
522 PluginFClient::~PluginFClient()
526 bool PluginFClient::is_audio(AVFilter *fp)
528 if( !fp->outputs ) return 0;
529 if( avfilter_pad_count(fp->outputs) > 1 ) return 0;
530 if( !avfilter_pad_get_name(fp->outputs, 0) ) return 0;
531 if( avfilter_pad_get_type(fp->outputs, 0) != AVMEDIA_TYPE_AUDIO ) return 0;
532 if( !fp->inputs ) return 1;
533 if( avfilter_pad_count(fp->inputs) > 1 ) return 0;
534 if( !avfilter_pad_get_name(fp->inputs, 0) ) return 0;
535 if( avfilter_pad_get_type(fp->inputs, 0) != AVMEDIA_TYPE_AUDIO ) return 0;
538 bool PluginFClient::is_video(AVFilter *fp)
540 if( !fp->outputs ) return 0;
541 if( avfilter_pad_count(fp->outputs) > 1 ) return 0;
542 if( !avfilter_pad_get_name(fp->outputs, 0) ) return 0;
543 if( avfilter_pad_get_type(fp->outputs, 0) != AVMEDIA_TYPE_VIDEO ) return 0;
544 if( !fp->inputs ) return 1;
545 if( avfilter_pad_count(fp->inputs) > 1 ) return 0;
546 if( !avfilter_pad_get_name(fp->inputs, 0) ) return 0;
547 if( avfilter_pad_get_type(fp->inputs, 0) != AVMEDIA_TYPE_VIDEO ) return 0;
551 NEW_WINDOW_MACRO(PluginFClient, PluginFClientWindow)
553 int PluginFClient::load_configuration()
555 int64_t src_position = get_source_position();
556 KeyFrame *prev_keyframe = get_prev_keyframe(src_position);
557 config.copy_from(curr_config);
558 read_data(prev_keyframe);
559 int ret = !config.equivalent(curr_config) ? 1 : 0;
564 void PluginFClient::render_gui(void *data, int size)
566 PluginFClientConfig *conf = (PluginFClientConfig *)data;
567 config.copy_from(*conf);
568 KeyFrame *keyframe = plugin->server->get_keyframe();
573 void PluginFClient::update_gui()
575 PluginClientThread *thread = plugin->get_thread();
576 if( !thread ) return;
577 PluginFClientWindow *window = (PluginFClientWindow*)thread->get_window();
578 window->lock_window("PluginFClient::update_gui");
579 load_configuration();
580 if( config.update() > 0 )
582 window->unlock_window();
585 const char *PluginFClient::plugin_title()
590 char *PluginFClient::to_upper(char *cp, const char *sp)
593 while( *sp != 0 ) *bp++ = toupper(*sp++);
598 void PluginFClient::save_data(KeyFrame *keyframe)
601 char string[BCTEXTLEN];
603 // cause data to be stored directly in text
604 output.set_shared_output(keyframe->get_data(), MESSAGESIZE);
605 output.tag.set_title(to_upper(string, plugin_title()));
606 const AVClass *filt_class = config.filter_class();
607 if( filt_class && filt_class->option ) {
608 void *obj = config.filter_config();
609 const AVOption *opt = NULL;
610 while( (opt=av_opt_next(obj, opt)) != 0 ) {
612 if( av_opt_get(obj, opt->name, 0, &buf) < 0 ) continue;
613 output.tag.set_property(opt->name, (const char *)buf);
619 output.terminate_string();
622 void PluginFClient::read_data(KeyFrame *keyframe)
625 char string[BCTEXTLEN], value[BCTEXTLEN];
626 input.set_shared_input(keyframe->get_data(), strlen(keyframe->get_data()));
628 while( !input.read_tag() ) {
629 to_upper(string, plugin_title());
630 if( !input.tag.title_is(string) ) continue;
631 const AVClass *filt_class = config.filter_class();
632 if( filt_class && filt_class->option ) {
633 void *obj = config.filter_config();
634 const AVOption *opt = NULL;
635 while( (opt=av_opt_next(obj, opt)) != 0 ) {
636 to_upper(string, opt->name);
637 if( !input.tag.get_property(string, value) ) continue;
638 av_opt_set(obj, opt->name, value, 0);
644 int PluginFClient::activate()
647 avfilter_link(fsrc, 0, ffilt->fctx, 0);
648 avfilter_link(ffilt->fctx, 0, fsink, 0);
649 int ret = avfilter_graph_config(ffilt->graph, NULL);
651 curr_config.copy_from(config);
657 void PluginFClient::reactivate()
659 delete ffilt; ffilt = 0;
663 PluginFAClient::PluginFAClient(PluginServer *server, const char *name)
664 : PluginAClient(server), PluginFClient(this, name)
668 PluginFAClient::~PluginFAClient()
672 int PluginFAClient::activate()
674 if( activated ) return activated;
675 ffilt = PluginFFilter::new_ffilter(name, &config);
677 config.copy_from(curr_config);
678 send_configure_change();
679 send_render_gui(&config, sizeof(config));
680 ffilt = PluginFFilter::new_ffilter(name, &config);
682 AVSampleFormat sample_fmt = AV_SAMPLE_FMT_FLTP;
683 int channels = PluginClient::total_in_buffers;
684 uint64_t layout = (((uint64_t)1)<<channels) - 1;
685 AVFilterGraph *graph = !ffilt ? 0 : ffilt->graph;
686 int ret = !graph ? -1 : 0;
687 if( ret >= 0 && ffilt->filter->inputs ) {
688 char args[BCTEXTLEN];
689 snprintf(args, sizeof(args),
690 "time_base=%d/%d:sample_rate=%d:sample_fmt=%s:channel_layout=0x%jx",
691 1, sample_rate, sample_rate, av_get_sample_fmt_name(sample_fmt), layout);
692 ret = avfilter_graph_create_filter(&fsrc, avfilter_get_by_name("abuffer"),
693 "in", args, NULL, graph);
696 ret = avfilter_graph_create_filter(&fsink, avfilter_get_by_name("abuffersink"),
697 "out", NULL, NULL, graph);
699 ret = av_opt_set_bin(fsink, "sample_fmts",
700 (uint8_t*)&sample_fmt, sizeof(sample_fmt), AV_OPT_SEARCH_CHILDREN);
702 ret = av_opt_set_bin(fsink, "channel_layouts",
703 (uint8_t*)&layout, sizeof(layout), AV_OPT_SEARCH_CHILDREN);
705 ret = av_opt_set_bin(fsink, "sample_rates",
706 (uint8_t*)&sample_rate, sizeof(sample_rate), AV_OPT_SEARCH_CHILDREN);
708 ret = PluginFClient::activate();
709 if( ret < 0 && activated >= 0 ) {
710 ff_err(ret, "PluginFAClient::activate: %s failed\n", plugin_title());
717 static AVRational best_frame_rate(double frame_rate)
719 static const int m1 = 1001*12, m2 = 1000*12;
720 static const int freqs[] = {
721 40*m1, 48*m1, 50*m1, 60*m1, 80*m1,120*m1, 240*m1,
722 24*m2, 30*m2, 60*m2, 12*m2, 15*m2, 48*m2, 0,
725 int freq, best_freq = 0;
726 for( int i=0; (freq = i<30*12 ? (i+1)*1001 : freqs[i-30*12]); ++i ) {
727 double framerate = (double)freq / m1;
728 double err = fabs(frame_rate/framerate - 1.);
729 if( err >= max_err ) continue;
733 return max_err < 0.0001 ?
734 (AVRational) { best_freq, m1 } :
735 (AVRational) { 0, 0 };
738 int PluginFVClient::activate(int width, int height, int color_model)
740 if( activated ) return activated;
741 ffilt = PluginFFilter::new_ffilter(name, &config);
743 config.copy_from(curr_config);
744 send_configure_change();
745 send_render_gui(&config, sizeof(config));
746 ffilt = PluginFFilter::new_ffilter(name, &config);
748 AVPixelFormat pix_fmt = color_model_to_pix_fmt(color_model);
749 AVFilterGraph *graph = !ffilt ? 0 : ffilt->graph;
750 int ret = !graph ? -1 : 0;
751 if( ret >= 0 && ffilt->filter->inputs ) {
752 curr_config.copy_from(config);
753 if( pix_fmt == AV_PIX_FMT_NB ) {
754 int bpp = BC_CModels::calculate_pixelsize(color_model) * 8;
755 int bits_per_comp = bpp / BC_CModels::components(color_model);
756 int alpha = BC_CModels::has_alpha(color_model);
757 pix_fmt = bits_per_comp > 8 ?
758 !alpha ? AV_PIX_FMT_RGB48LE : AV_PIX_FMT_RGBA64LE :
759 !alpha ? AV_PIX_FMT_RGB24 : AV_PIX_FMT_RGBA ;
761 int aspect_w = 1, aspect_h = 1; // XXX
762 AVRational best_rate = best_frame_rate(frame_rate);
763 char args[BCTEXTLEN];
764 snprintf(args, sizeof(args),
765 "video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d",
766 width, height, pix_fmt, best_rate.num, best_rate.den, aspect_w, aspect_h);
767 ret = avfilter_graph_create_filter(&fsrc, avfilter_get_by_name("buffer"),
768 "in", args, NULL, graph);
771 ret = avfilter_graph_create_filter(&fsink, avfilter_get_by_name("buffersink"),
772 "out", NULL, NULL, graph);
774 ret = av_opt_set_bin(fsink, "pix_fmts",
775 (uint8_t*)&pix_fmt, sizeof(pix_fmt), AV_OPT_SEARCH_CHILDREN);
777 ret = PluginFClient::activate();
778 if( ret < 0 && activated >= 0 ) {
779 ff_err(ret, "PluginFVClient::activate() %s\n", plugin_title());
785 int PluginFAClient::get_inchannels()
787 AVFilterContext *fctx = ffilt->fctx;
788 AVFilterLink **links = !fctx->nb_inputs ? 0 : fctx->inputs;
789 return !links ? 0 : avfilter_link_get_channels(links[0]);
792 int PluginFAClient::get_outchannels()
794 AVFilterContext *fctx = ffilt->fctx;
795 AVFilterLink **links = !fctx->nb_outputs ? 0 : fctx->outputs;
796 return !links ? 0 : avfilter_link_get_channels(links[0]);
799 int PluginFAClient::process_buffer(int64_t size, Samples **buffer, int64_t start_position, int sample_rate)
801 int total_in = PluginClient::total_in_buffers;
802 int total_out = PluginClient::total_out_buffers;
803 int in_channels = 0, out_channels = 0;
805 if( load_configuration() )
806 plugin_position = -1;
807 if( plugin_position != start_position ) {
808 filter_position = plugin_position = start_position;
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);
819 in_channels = get_inchannels();
820 out_channels = get_outchannels();
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);
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];
845 ret = av_buffersrc_add_frame_flags(fsrc, frame, 0);
847 if( ret >= 0 && retry < 0 )
848 ret = AVERROR(EAGAIN);
851 int nbfrs = total_out;
852 if( out_channels < nbfrs ) nbfrs = out_channels;
853 float **out_buffers = (float **)&frame->extended_data[0];
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];
860 while( i < total_out ) {
861 double *out_ptr = buffer[i++]->get_data();
862 bzero(out_ptr, sizeof(double) * size);
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;
875 ff_err(ret, "PluginFAClient::process_buffer() %s\n", plugin_title());
878 av_frame_free(&frame);
879 plugin_position += size;
884 PluginFVClient::PluginFVClient(PluginServer *server, const char *name)
885 : PluginVClient(server), PluginFClient(this, name)
889 PluginFVClient::~PluginFVClient()
893 int PluginFVClient::process_buffer(VFrame **frames, int64_t position, double frame_rate)
895 VFrame *vframe = *frames;
896 int width = vframe->get_w();
897 int height = vframe->get_h();
899 if( load_configuration() )
900 plugin_position = -1;
901 if( plugin_position != position ) {
902 filter_position = plugin_position = position;
906 int colormodel = vframe->get_color_model();
907 int ret = activate(width, height, colormodel);
908 AVPixelFormat pix_fmt = fsrc ?
909 (AVPixelFormat) fsrc->outputs[0]->format :
910 color_model_to_pix_fmt(colormodel);
911 if( pix_fmt <= AV_PIX_FMT_NONE || pix_fmt >= AV_PIX_FMT_NB )
912 pix_fmt = AV_PIX_FMT_RGBA;
915 if( ret >= 0 && !(frame = av_frame_alloc()) ) {
916 fprintf(stderr, "PluginFVClient::process_buffer: av_frame_alloc failed\n");
917 ret = AVERROR(ENOMEM);
921 while( ret >= 0 && --retry >= 0 ) {
922 ret = av_buffersink_get_frame(fsink, frame);
923 if( ret >= 0 || ret != AVERROR(EAGAIN) ) break;
924 if( !fsrc ) { ret = AVERROR(EIO); break; }
925 read_frame(vframe, 0, filter_position++, frame_rate, get_use_opengl());
926 frame->format = pix_fmt;
927 frame->width = width;
928 frame->height = height;
929 frame->pts = local_to_edl(position);
930 ret = av_frame_get_buffer(frame, 32);
932 ret = transfer_pixfmt(vframe, frame, pix_fmt, width, height);
934 ret = av_buffersrc_add_frame_flags(fsrc, frame, 0);
936 if( ret >= 0 && retry < 0 )
937 ret = AVERROR(EAGAIN);
940 pix_fmt = (AVPixelFormat) frame->format;
941 ret = transfer_cmodel(vframe, frame, pix_fmt, width, height);
944 ff_err(ret, "PluginFVClient::process_buffer() %s\n", plugin_title());
945 if( position & 1 ) vframe->clear_frame();
949 av_frame_free(&frame);
950 return ret >= 0 ? 0 : 1;
954 PluginFClient_OptName:: PluginFClient_OptName(PluginFClient_Opt *opt)
957 set_text(opt->opt->name);
960 PluginFClient_OptValue::PluginFClient_OptValue(PluginFClient_Opt *opt)
966 void PluginFClient_OptValue::update()
968 char val[BCTEXTLEN]; val[0] = 0;
969 opt->get(val, sizeof(val));
974 PluginFClient_Opt::PluginFClient_Opt(PluginFClientConfig *conf, const AVOption *opt)
978 item_name = new PluginFClient_OptName(this);
979 item_value = new PluginFClient_OptValue(this);
982 PluginFClient_Opt::~PluginFClient_Opt()
988 char *PluginFClient_Opt::get(char *vp, int sz)
991 void *obj = filter_config();
993 if( av_opt_get(obj, opt->name, 0, &bp) >= 0 && bp != 0 ) {
994 const char *val = (const char *)bp;
995 ret = sz >= 0 ? strncpy(vp,val,sz) : strcpy(vp, val);
996 if( sz > 0 ) vp[sz-1] = 0;
1001 void PluginFClient_Opt::set(const char *val)
1003 void *obj = filter_config();
1004 av_opt_set(obj , opt->name, val, 0);
1007 void PluginFFilter::uninit()
1011 static int get_defaults(const char *name, char *args)
1014 char defaults_path[BCTEXTLEN];
1015 FFMPEG::set_option_path(defaults_path, "plugin.opts");
1016 FILE *fp = fopen(defaults_path,"r");
1018 char ff_plugin[BCSTRLEN], ff_args[BCTEXTLEN], *ap = 0;
1019 while( !ap && fgets(ff_args, sizeof(ff_args), fp) ) {
1020 if( *(ap=ff_args) == ';' ) continue;
1021 if( *(ap=ff_args) == '#' ) ++ap;
1022 char *bp = ff_plugin, *ep = bp + sizeof(ff_plugin)-1;
1023 while( bp < ep && *ap && *ap != '\n' && *ap != ' ' ) *bp++ = *ap++;
1025 if( strcmp(ff_plugin, name) ) ap = 0;
1029 if( ff_args[0] == '#' ) return -1;
1030 while( *ap == ' ' ) ++ap;
1031 while( *ap && *ap != '\n' ) *args++ = *ap++;
1036 bool PluginFFilter::is_audio()
1038 return PluginFClient::is_audio(filter);
1041 bool PluginFFilter::is_video()
1043 return PluginFClient::is_video(filter);
1046 int PluginFFilter::init(const char *name, PluginFClientConfig *conf)
1048 char args[BCTEXTLEN];
1049 int ret = get_defaults(name, args);
1050 if( ret < 0 ) return 0;
1051 PluginFLogLevel errs(AV_LOG_ERROR);
1052 this->filter = avfilter_get_by_name(name);
1053 if( !this->filter ) return AVERROR(ENOENT);
1054 int flag_mask = AVFILTER_FLAG_DYNAMIC_INPUTS | AVFILTER_FLAG_DYNAMIC_OUTPUTS;
1055 if( filter->flags & flag_mask ) return AVERROR(EPERM);
1056 if( !this->is_audio() && !this->is_video() ) return AVERROR(EIO);
1057 this->graph = avfilter_graph_alloc();
1058 if( !this->graph ) return AVERROR(ENOMEM);
1059 static int inst = 0;
1060 char inst_name[BCSTRLEN];
1061 sprintf(inst_name,"%s_%d", name, ++inst);
1062 graph->thread_type = 0;
1063 graph->nb_threads = 1;
1064 fctx = avfilter_graph_alloc_filter(graph, filter, inst_name);
1065 if( !fctx ) return AVERROR(ENOMEM);
1067 AVDictionary *opts = 0;
1068 for( int i=0; i<conf->size(); ++i ) {
1069 PluginFClient_Opt *op = conf->get(i);
1070 const char *name = op->opt->name;
1071 char val[BCTEXTLEN], *vp = op->get(val, sizeof(val));
1072 if( vp ) av_dict_set(&opts, name, vp, 0);
1074 ret = avfilter_init_dict(fctx, &opts);
1075 av_dict_free(&opts);
1078 ret = avfilter_init_str(fctx, args);
1079 return ret < 0 ? ret : 1;
1083 PluginFFilter::PluginFFilter()
1090 PluginFFilter::~PluginFFilter()
1092 PluginFLogLevel errs(AV_LOG_ERROR);
1093 avfilter_graph_free(&graph);
1094 filter = 0; fctx = 0;
1097 PluginFFilter *PluginFFilter::new_ffilter(const char *name, PluginFClientConfig *conf)
1099 PluginFFilter *ffilt = new PluginFFilter;
1100 int ret = ffilt->init(name, conf);
1101 if( ret < 0 ) ff_err(ret, "PluginFFilter::new_ffilter(%s)\n", name);
1102 if( ret <= 0 ) { delete ffilt; ffilt = 0; }
1106 PluginClient *PluginServer::new_ffmpeg_plugin()
1108 AVFilter *filter = avfilter_get_by_name(ff_name);
1109 if( !filter ) return 0;
1110 PluginFClient *ffmpeg =
1111 PluginFClient::is_audio(filter) ?
1112 (PluginFClient *)new PluginFAClient(this, ff_name) :
1113 PluginFClient::is_video(filter) ?
1114 (PluginFClient *)new PluginFVClient(this, ff_name) :
1116 if( !ffmpeg ) return 0;
1117 return ffmpeg->plugin;
1121 PluginServer* MWindow::new_ffmpeg_server(MWindow *mwindow, const char *name)
1123 PluginFFilter *ffilt = PluginFFilter::new_ffilter(name);
1124 if( !ffilt ) return 0;
1126 return new PluginServer(mwindow, (char*)name, PLUGIN_TYPE_FFMPEG);
1129 void MWindow::init_ffmpeg_index(MWindow *mwindow, Preferences *preferences, FILE *fp)
1131 PluginFLogLevel errs(AV_LOG_ERROR);
1132 const AVFilter *filter = 0;
1133 while( (filter=avfilter_next(filter)) != 0 ) {
1134 PluginServer *server = new_ffmpeg_server(mwindow, filter->name);
1136 int result = server->open_plugin(1, preferences, 0, 0);
1138 server->write_table(fp, PLUGIN_FFMPEG_ID);
1139 server->close_plugin();
1141 server->delete_this();
1142 if( result ) fprintf(fp, "#%s\n", filter->name);
1147 void MWindow::init_ffmpeg()
1150 avfilter_register_all();