1 // plugins.c
2 // LiVES
3 // (c) G. Finch 2003 - 2020 <salsaman+lives@gmail.com>
4 // released under the GNU GPL 3 or later
5 // see file ../COPYING or www.gnu.org for licensing details
6
7 #include <dlfcn.h>
8 #include <errno.h>
9
10 #include "main.h"
11 #include "resample.h"
12 #include "effects.h"
13 #include "interface.h"
14
15 #include "rfx-builder.h"
16 #include "paramwindow.h"
17
18 #include "lsd-tab.h"
19
20 // *INDENT-OFF*
21 const char *const anames[AUDIO_CODEC_MAX] = {"mp3", "pcm", "mp2", "vorbis", "AC3", "AAC", "AMR_NB",
22 "raw", "wma2", "opus", ""};
23 // *INDENT-ON*
24
25 static boolean list_plugins;
26
27 ///////////////////////
28 // command-line plugins
29
get_plugin_result(const char * command,const char * delim,boolean allow_blanks,boolean strip)30 LiVESList *get_plugin_result(const char *command, const char *delim, boolean allow_blanks, boolean strip) {
31 LiVESList *list = NULL;
32 char buffer[65536];
33
34 //threaded_dialog_spin(0.);
35
36 lives_popen(command, !mainw->is_ready && !list_plugins, buffer, 65535);
37
38 if (THREADVAR(com_failed)) return NULL;
39
40 //threaded_dialog_spin(0.);
41 list = buff_to_list(buffer, delim, allow_blanks, strip);
42 //threaded_dialog_spin(0.);
43 return list;
44 }
45
46
plugin_request_with_blanks(const char * plugin_type,const char * plugin_name,const char * request)47 LIVES_GLOBAL_INLINE LiVESList *plugin_request_with_blanks(const char *plugin_type, const char *plugin_name,
48 const char *request) {
49 // allow blanks in a list
50 return plugin_request_common(plugin_type, plugin_name, request, "|", TRUE);
51 }
52
53
plugin_request(const char * plugin_type,const char * plugin_name,const char * request)54 LIVES_GLOBAL_INLINE LiVESList *plugin_request(const char *plugin_type, const char *plugin_name, const char *request) {
55 return plugin_request_common(plugin_type, plugin_name, request, "|", FALSE);
56 }
57
58
plugin_request_by_line(const char * plugin_type,const char * plugin_name,const char * request)59 LIVES_GLOBAL_INLINE LiVESList *plugin_request_by_line(const char *plugin_type, const char *plugin_name, const char *request) {
60 return plugin_request_common(plugin_type, plugin_name, request, "\n", FALSE);
61 }
62
63
plugin_request_by_space(const char * plugin_type,const char * plugin_name,const char * request)64 LIVES_GLOBAL_INLINE LiVESList *plugin_request_by_space(const char *plugin_type, const char *plugin_name, const char *request) {
65 return plugin_request_common(plugin_type, plugin_name, request, " ", FALSE);
66 }
67
68
plugin_request_common(const char * plugin_type,const char * plugin_name,const char * request,const char * delim,boolean allow_blanks)69 LiVESList *plugin_request_common(const char *plugin_type, const char *plugin_name, const char *request,
70 const char *delim, boolean allow_blanks) {
71 // returns a LiVESList of responses to -request, or NULL on error
72
73 // NOTE: request must not be quoted here, since it contains a list of parameters
74 // instead, caller should ensure that any strings in *request are suitably escaped and quoted
75 // e.g. by calling param_marshall()
76
77 LiVESList *reslist = NULL;
78 char *com, *comfile;
79
80 if (plugin_type) {
81 if (!plugin_name || !strlen(plugin_name)) {
82 return reslist;
83 }
84
85 // some types live in the config directory...
86 if (!strcmp(plugin_type, PLUGIN_RENDERED_EFFECTS_CUSTOM) || !strcmp(plugin_type, PLUGIN_RENDERED_EFFECTS_TEST)) {
87 comfile = lives_build_filename(prefs->config_datadir, plugin_type, plugin_name, NULL);
88 } else if (!strcmp(plugin_type, PLUGIN_RFX_SCRAP)) {
89 // scraps are in the workdir
90 comfile = lives_build_filename(prefs->workdir, plugin_name, NULL);
91 } else {
92 comfile = lives_build_filename(prefs->lib_dir, PLUGIN_EXEC_DIR, plugin_type, plugin_name, NULL);
93 }
94
95 //#define DEBUG_PLUGINS
96 #ifdef DEBUG_PLUGINS
97 com = lives_strdup_printf("\"%s\" %s", comfile, request);
98 lives_printerr("will run: %s\n", com);
99 #else
100 com = lives_strdup_printf("\"%s\" %s 2>/dev/null", comfile, request);
101 #endif
102 lives_free(comfile);
103 } else com = lives_strdup(request);
104 list_plugins = FALSE;
105 reslist = get_plugin_result(com, delim, allow_blanks, TRUE);
106 lives_free(com);
107 //threaded_dialog_spin(0.);
108 return reslist;
109 }
110
111
112 //////////////////
113 // get list of plugins of various types
114
get_plugin_list(const char * plugin_type,boolean allow_nonex,const char * plugdir,const char * filter_ext)115 LiVESList *get_plugin_list(const char *plugin_type, boolean allow_nonex, const char *plugdir, const char *filter_ext) {
116 // returns a LiVESList * of plugins of type plugin_type
117 // returns empty list if there are no plugins of that type
118
119 // allow_nonex to allow non-executable files (e.g. libs)
120 // filter_ext can be non-NULL to filter for files ending .filter_ext
121
122 // TODO - use enum for plugin_type
123
124 // format is: allow_nonex (0 or 1) allow_subdirs (0 or 1) plugindir ext
125
126 char *com, *tmp;
127 LiVESList *pluglist;
128
129 const char *ext = (filter_ext == NULL) ? "" : filter_ext;
130
131 if (!strcmp(plugin_type, PLUGIN_THEMES)) {
132 // must not allow_nonex, otherwise we get splash image etc (just want dirs)
133 com = lives_strdup_printf("%s list_plugins 0 1 \"%s%s\" \"\"", prefs->backend_sync, prefs->prefix_dir, THEME_DIR);
134 } else if (!strcmp(plugin_type, PLUGIN_RENDERED_EFFECTS_CUSTOM_SCRIPTS) ||
135 !strcmp(plugin_type, PLUGIN_RENDERED_EFFECTS_TEST_SCRIPTS) ||
136 !strcmp(plugin_type, PLUGIN_RENDERED_EFFECTS_CUSTOM) ||
137 !strcmp(plugin_type, PLUGIN_RENDERED_EFFECTS_TEST) ||
138 !strcmp(plugin_type, PLUGIN_COMPOUND_EFFECTS_CUSTOM)
139 ) {
140 // look in home
141 tmp = lives_build_path(prefs->config_datadir, plugin_type, NULL);
142 com = lives_strdup_printf("%s list_plugins %d 0 \"%s\" \"%s\"", prefs->backend_sync, allow_nonex, tmp, ext);
143 lives_free(tmp);
144 } else if (!strcmp(plugin_type, PLUGIN_THEMES_CUSTOM)) {
145 tmp = lives_build_path(prefs->config_datadir, PLUGIN_THEMES, NULL);
146 com = lives_strdup_printf("%s list_plugins 0 1 \"%s\"", prefs->backend_sync, tmp);
147 lives_free(tmp);
148 } else if (!strcmp(plugin_type, PLUGIN_EFFECTS_WEED)) {
149 com = lives_strdup_printf("%s list_plugins 1 1 \"%s\" \"%s\"", prefs->backend_sync,
150 (tmp = lives_filename_from_utf8((char *)plugdir, -1, NULL, NULL, NULL)), ext);
151 lives_free(tmp);
152 } else if (!strcmp(plugin_type, PLUGIN_DECODERS)) {
153 com = lives_strdup_printf("%s list_plugins 1 0 \"%s\" \"%s\"", prefs->backend_sync,
154 (tmp = lives_filename_from_utf8((char *)plugdir, -1, NULL, NULL, NULL)), ext);
155 lives_free(tmp);
156 } else if (!strcmp(plugin_type, PLUGIN_RENDERED_EFFECTS_BUILTIN_SCRIPTS)) {
157 com = lives_strdup_printf("%s list_plugins %d 0 \"%s%s%s\" \"%s\"", prefs->backend_sync, allow_nonex, prefs->prefix_dir,
158 PLUGIN_SCRIPTS_DIR, plugin_type, ext);
159 } else if (!strcmp(plugin_type, PLUGIN_COMPOUND_EFFECTS_BUILTIN)) {
160 com = lives_strdup_printf("%s list_plugins %d 0 \"%s%s%s\" \"%s\"", prefs->backend_sync, allow_nonex, prefs->prefix_dir,
161 PLUGIN_COMPOUND_DIR, plugin_type, ext);
162 } else {
163 com = lives_strdup_printf("%s list_plugins %d 0 \"%s%s%s\" \"%s\"", prefs->backend_sync, allow_nonex, prefs->lib_dir,
164 PLUGIN_EXEC_DIR, plugin_type, ext);
165 }
166 list_plugins = TRUE;
167
168 //g_print("\n\n\nLIST CMD: %s\n",com);
169
170 pluglist = get_plugin_result(com, "|", FALSE, TRUE);
171 lives_free(com);
172 //threaded_dialog_spin(0.);
173 pluglist = lives_list_sort_alpha(pluglist, TRUE);
174 return pluglist;
175 }
176
177
178 ///////////////////
179 // video plugins
180
save_vpp_defaults(_vid_playback_plugin * vpp,char * vpp_file)181 void save_vpp_defaults(_vid_playback_plugin *vpp, char *vpp_file) {
182 // format is:
183 // nbytes (string) : LiVES vpp defaults file version 2\n
184 // for each video playback plugin:
185 // 4 bytes (int) name length
186 // n bytes name
187 // 4 bytes (int) version length
188 // n bytes version
189 // 4 bytes (int) palette
190 // 4 bytes (int) YUV sampling
191 // 4 bytes (int) YUV clamping
192 // 4 bytes (int) YUV subspace
193 // 4 bytes (int) width
194 // 4 bytes (int) height
195 // 8 bytes (double) fps
196 // 4 bytes (int) fps_numerator [0 indicates use fps double, >0 use fps_numer/fps_denom ]
197 // 4 bytes (int) fps_denominator
198 // 4 bytes argc (count of argv, may be 0)
199 //
200 // for each argv (extra params):
201 // 4 bytes (int) length
202 // n bytes string param value
203
204 int fd;
205 int32_t len;
206 const char *version;
207 int i;
208 char *msg;
209 int intzero = 0;
210 double dblzero = 0.;
211
212 if (!vpp) {
213 lives_rm(vpp_file);
214 return;
215 }
216
217 if ((fd = lives_open3(vpp_file, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR)) == -1) {
218 msg = lives_strdup_printf(_("\n\nUnable to write video playback plugin defaults file\n%s\nError code %d\n"), vpp_file, errno);
219 LIVES_ERROR(msg);
220 lives_free(msg);
221 return;
222 }
223
224 msg = lives_strdup_printf(_("Updating video playback plugin defaults in %s\n"), vpp_file);
225 LIVES_INFO(msg);
226 lives_free(msg);
227
228 msg = lives_strdup("LiVES vpp defaults file version 2\n");
229 if (!lives_write(fd, msg, strlen(msg), FALSE)) return;
230 lives_free(msg);
231
232 len = strlen(vpp->name);
233 if (lives_write_le(fd, &len, 4, FALSE) < 4) return;
234 if (lives_write(fd, vpp->name, len, FALSE) < len) return;
235
236 version = (*vpp->version)();
237 len = strlen(version);
238 if (lives_write_le(fd, &len, 4, FALSE) < 4) return;
239 if (lives_write(fd, version, len, FALSE) < len) return;
240
241 if (lives_write_le(fd, &(vpp->palette), 4, FALSE) < 4) return;
242 if (lives_write_le(fd, &(vpp->YUV_sampling), 4, FALSE) < 4) return;
243 if (lives_write_le(fd, &(vpp->YUV_clamping), 4, FALSE) < 4) return;
244 if (lives_write_le(fd, &(vpp->YUV_subspace), 4, FALSE) < 4) return;
245
246 if (lives_write_le(fd, vpp->fwidth <= 0 ? &intzero : & (vpp->fwidth), 4, FALSE) < 4) return;
247 if (lives_write_le(fd, vpp->fheight <= 0 ? &intzero : & (vpp->fheight), 4, FALSE) < 4) return;
248
249 if (lives_write_le(fd, vpp->fixed_fpsd <= 0. ? &dblzero : & (vpp->fixed_fpsd), 8, FALSE) < 8) return;
250 if (lives_write_le(fd, vpp->fixed_fps_numer <= 0 ? &intzero : & (vpp->fixed_fps_numer), 4, FALSE) < 4) return;
251 if (lives_write_le(fd, vpp->fixed_fps_denom <= 0 ? &intzero : & (vpp->fixed_fps_denom), 4, FALSE) < 4) return;
252
253 if (lives_write_le(fd, &(vpp->extra_argc), 4, FALSE) < 4) return;
254
255 for (i = 0; i < vpp->extra_argc; i++) {
256 len = strlen(vpp->extra_argv[i]);
257 if (lives_write_le(fd, &len, 4, FALSE) < 4) return;
258 if (lives_write(fd, vpp->extra_argv[i], len, FALSE) < len) return;
259 }
260
261 close(fd);
262 }
263
264
load_vpp_defaults(_vid_playback_plugin * vpp,char * vpp_file)265 void load_vpp_defaults(_vid_playback_plugin *vpp, char *vpp_file) {
266 ssize_t len;
267 const char *version;
268
269 char buf[512];
270
271 char *msg;
272
273 int retval;
274 int fd;
275
276 register int i;
277
278 if (!lives_file_test(vpp_file, LIVES_FILE_TEST_EXISTS)) {
279 return;
280 }
281
282 d_print(_("Loading video playback plugin defaults from %s..."), vpp_file);
283
284 do {
285 retval = 0;
286 if ((fd = lives_open2(vpp_file, O_RDONLY)) == -1) {
287 retval = do_read_failed_error_s_with_retry(vpp_file, lives_strerror(errno));
288 if (retval == LIVES_RESPONSE_CANCEL) {
289 vpp = NULL;
290 return;
291 }
292 } else {
293 do {
294 // only do this loop once, so we can use break to escape it
295
296 THREADVAR(read_failed) = FALSE;
297 msg = lives_strdup("LiVES vpp defaults file version 2\n");
298 len = lives_read(fd, buf, strlen(msg), FALSE);
299 if (len < 0) len = 0;
300 lives_memset(buf + len, 0, 1);
301
302 if (THREADVAR(read_failed)) break;
303
304 // identifier string
305 if (strcmp(msg, buf)) {
306 lives_free(msg);
307 d_print_file_error_failed();
308 close(fd);
309 return;
310 }
311 lives_free(msg);
312
313 // plugin name
314 lives_read_le(fd, &len, 4, FALSE);
315 if (THREADVAR(read_failed)) break;
316 lives_read(fd, buf, len, FALSE);
317 lives_memset(buf + len, 0, 1);
318
319 if (THREADVAR(read_failed)) break;
320
321 if (strcmp(buf, vpp->name)) {
322 d_print_file_error_failed();
323 close(fd);
324 return;
325 }
326
327 // version string
328 version = (*vpp->version)();
329 lives_read_le(fd, &len, 4, FALSE);
330 if (THREADVAR(read_failed)) break;
331 lives_read(fd, buf, len, FALSE);
332
333 if (THREADVAR(read_failed)) break;
334
335 lives_memset(buf + len, 0, 1);
336
337 if (strcmp(buf, version)) {
338 msg = lives_strdup_printf(
339 _("\nThe %s video playback plugin has been updated.\nPlease check your settings in\n"
340 "Tools|Preferences|Playback|Playback Plugins Advanced\n\n"),
341 vpp->name);
342 widget_opts.non_modal = TRUE;
343 do_error_dialog(msg);
344 widget_opts.non_modal = FALSE;
345 lives_free(msg);
346 lives_rm(vpp_file);
347 d_print_failed();
348 close(fd);
349 return;
350 }
351
352 lives_read_le(fd, &(vpp->palette), 4, FALSE);
353 lives_read_le(fd, &(vpp->YUV_sampling), 4, FALSE);
354 lives_read_le(fd, &(vpp->YUV_clamping), 4, FALSE);
355 lives_read_le(fd, &(vpp->YUV_subspace), 4, FALSE);
356 lives_read_le(fd, &(vpp->fwidth), 4, FALSE);
357 lives_read_le(fd, &(vpp->fheight), 4, FALSE);
358 lives_read_le(fd, &(vpp->fixed_fpsd), 8, FALSE);
359 lives_read_le(fd, &(vpp->fixed_fps_numer), 4, FALSE);
360 lives_read_le(fd, &(vpp->fixed_fps_denom), 4, FALSE);
361
362 if (THREADVAR(read_failed)) break;
363
364 lives_read_le(fd, &(vpp->extra_argc), 4, FALSE);
365
366 if (THREADVAR(read_failed)) break;
367
368 if (vpp->extra_argv) {
369 for (i = 0; vpp->extra_argv[i]; i++) {
370 lives_free(vpp->extra_argv[i]);
371 }
372 lives_free(vpp->extra_argv);
373 }
374
375 vpp->extra_argv = (char **)lives_calloc((vpp->extra_argc + 1), (sizeof(char *)));
376
377 for (i = 0; i < vpp->extra_argc; i++) {
378 lives_read_le(fd, &len, 4, FALSE);
379 if (THREADVAR(read_failed)) break;
380 vpp->extra_argv[i] = (char *)lives_malloc(len + 1);
381 lives_read(fd, vpp->extra_argv[i], len, FALSE);
382 if (THREADVAR(read_failed)) break;
383 lives_memset((vpp->extra_argv[i]) + len, 0, 1);
384 }
385
386 vpp->extra_argv[i] = NULL;
387
388 close(fd);
389 } while (FALSE);
390
391 if (THREADVAR(read_failed)) {
392 close(fd);
393 retval = do_read_failed_error_s_with_retry(vpp_file, NULL);
394 if (retval == LIVES_RESPONSE_CANCEL) {
395 THREADVAR(read_failed) = FALSE;
396 vpp = NULL;
397 d_print_file_error_failed();
398 return;
399 }
400 }
401 }
402 } while (retval == LIVES_RESPONSE_RETRY);
403
404 d_print_done();
405 }
406
407
on_vppa_cancel_clicked(LiVESButton * button,livespointer user_data)408 void on_vppa_cancel_clicked(LiVESButton *button, livespointer user_data) {
409 _vppaw *vppw = (_vppaw *)user_data;
410 _vid_playback_plugin *vpp = vppw->plugin;
411
412 lives_widget_destroy(vppw->dialog);
413 lives_widget_context_update();
414 if (vpp && vpp != mainw->vpp) {
415 // close the temp current vpp
416 close_vid_playback_plugin(vpp);
417 }
418
419 if (vppw->rfx) {
420 if (!vppw->keep_rfx) {
421 rfx_free(vppw->rfx);
422 lives_free(vppw->rfx);
423 }
424 }
425
426 lives_free(vppw);
427
428 if (prefsw) {
429 lives_window_present(LIVES_WINDOW(prefsw->prefs_dialog));
430 lives_xwindow_raise(lives_widget_get_xwindow(prefsw->prefs_dialog));
431 }
432 }
433
434
on_vppa_ok_clicked(LiVESButton * button,livespointer user_data)435 void on_vppa_ok_clicked(LiVESButton *button, livespointer user_data) {
436 _vppaw *vppw = (_vppaw *)user_data;
437 uint64_t xwinid = 0;
438
439 const char *fixed_fps = NULL;
440 const char *tmp;
441
442 char *cur_pal = NULL;
443
444 int *pal_list, i = 0;
445
446 boolean ext_audio = FALSE;
447
448 _vid_playback_plugin *vpp = vppw->plugin;
449
450 if (vppw->rfx && mainw->textwidget_focus) {
451 // make sure text widgets are updated if they activate the default
452 LiVESWidget *textwidget = (LiVESWidget *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(mainw->textwidget_focus),
453 TEXTWIDGET_KEY);
454 after_param_text_changed(textwidget, vppw->rfx);
455 }
456
457 if (!special_cleanup(TRUE)) {
458 // check for file overwrites with special type "filewrite"
459 // if user declines, will return with LIVES_RESPONSE_RETRY
460 if (LIVES_IS_DIALOG(lives_widget_get_toplevel(LIVES_WIDGET(button))))
461 lives_dialog_response(LIVES_DIALOG(lives_widget_get_toplevel(LIVES_WIDGET(button))), LIVES_RESPONSE_RETRY);
462 return;
463 }
464
465 mainw->textwidget_focus = NULL;
466
467 if (vpp == mainw->vpp) {
468 if (vppw->spinbuttonw) mainw->vpp->fwidth
469 = lives_spin_button_get_value_as_int(LIVES_SPIN_BUTTON(vppw->spinbuttonw));
470 if (vppw->spinbuttonh) mainw->vpp->fheight
471 = lives_spin_button_get_value_as_int(LIVES_SPIN_BUTTON(vppw->spinbuttonh));
472 if (vppw->apply_fx) mainw->fx1_bool = lives_toggle_button_get_active(LIVES_TOGGLE_BUTTON(vppw->apply_fx));
473 if (vppw->fps_entry) fixed_fps = lives_entry_get_text(LIVES_ENTRY(vppw->fps_entry));
474 if (vppw->pal_entry) {
475 cur_pal = lives_strdup(lives_entry_get_text(LIVES_ENTRY(vppw->pal_entry)));
476
477 if (get_token_count(cur_pal, ' ') > 1) {
478 char **array = lives_strsplit(cur_pal, " ", 2);
479 char *clamping = lives_strdup(array[1] + 1);
480 lives_free(cur_pal);
481 cur_pal = lives_strdup(array[0]);
482 lives_memset(clamping + strlen(clamping) - 1, 0, 1);
483 do {
484 tmp = weed_yuv_clamping_get_name(i);
485 if (tmp && !strcmp(clamping, tmp)) {
486 vpp->YUV_clamping = i;
487 break;
488 }
489 i++;
490 } while (tmp);
491 lives_strfreev(array);
492 lives_free(clamping);
493 }
494 }
495
496 if (vppw->fps_entry) {
497 if (!strlen(fixed_fps)) {
498 mainw->vpp->fixed_fpsd = -1.;
499 mainw->vpp->fixed_fps_numer = 0;
500 } else {
501 if (get_token_count((char *)fixed_fps, ':') > 1) {
502 char **array = lives_strsplit(fixed_fps, ":", 2);
503 mainw->vpp->fixed_fps_numer = atoi(array[0]);
504 mainw->vpp->fixed_fps_denom = atoi(array[1]);
505 lives_strfreev(array);
506 mainw->vpp->fixed_fpsd = get_ratio_fps((char *)fixed_fps);
507 } else {
508 mainw->vpp->fixed_fpsd = lives_strtod(fixed_fps, NULL);
509 mainw->vpp->fixed_fps_numer = 0;
510 }
511 }
512 } else {
513 mainw->vpp->fixed_fpsd = -1.;
514 mainw->vpp->fixed_fps_numer = 0;
515 }
516
517 if (mainw->vpp->fixed_fpsd > 0. && (mainw->fixed_fpsd > 0. ||
518 (mainw->vpp->set_fps &&
519 !((*mainw->vpp->set_fps)(mainw->vpp->fixed_fpsd))))) {
520 do_vpp_fps_error();
521 mainw->error = TRUE;
522 mainw->vpp->fixed_fpsd = -1.;
523 mainw->vpp->fixed_fps_numer = 0;
524 }
525
526 if (vppw->pal_entry) {
527 if (vpp->get_palette_list && (pal_list = (*vpp->get_palette_list)())) {
528 for (i = 0; pal_list[i] != WEED_PALETTE_END; i++) {
529 if (!strcmp(cur_pal, weed_palette_get_name(pal_list[i]))) {
530 vpp->palette = pal_list[i];
531 if (mainw->ext_playback) {
532 pthread_mutex_lock(&mainw->vpp_stream_mutex);
533 mainw->ext_audio = FALSE;
534 pthread_mutex_unlock(&mainw->vpp_stream_mutex);
535 lives_grab_remove(LIVES_MAIN_WINDOW_WIDGET);
536 if (mainw->vpp->exit_screen) {
537 (*mainw->vpp->exit_screen)(mainw->ptr_x, mainw->ptr_y);
538 }
539
540 #ifdef RT_AUDIO
541 stop_audio_stream();
542 #endif
543 mainw->stream_ticks = -1;
544 mainw->vpp->palette = pal_list[i];
545 if (!(*vpp->set_palette)(vpp->palette)) {
546 do_vpp_palette_error();
547 mainw->error = TRUE;
548 }
549
550 if (prefs->play_monitor != 0) {
551 if (mainw->play_window) {
552 xwinid = lives_widget_get_xwinid(mainw->play_window, "Unsupported display type for playback plugin");
553 if (xwinid == -1) {
554 lives_dialog_response(LIVES_DIALOG(vppw->dialog), LIVES_RESPONSE_CANCEL);
555 return;
556 }
557 }
558 }
559
560 #ifdef RT_AUDIO
561 if (vpp->set_yuv_palette_clamping)(*vpp->set_yuv_palette_clamping)(vpp->YUV_clamping);
562
563 if (mainw->vpp->audio_codec != AUDIO_CODEC_NONE && prefs->stream_audio_out) {
564 start_audio_stream();
565 }
566
567 #endif
568 if (vpp->init_audio && prefs->stream_audio_out) {
569 #ifdef HAVE_PULSE_AUDIO
570 if (prefs->audio_player == AUD_PLAYER_PULSE && mainw->pulsed) {
571 if ((*vpp->init_audio)(mainw->pulsed->out_arate, mainw->pulsed->out_achans, vpp->extra_argc, vpp->extra_argv))
572 mainw->ext_audio = TRUE;
573 }
574 #endif
575 #ifdef ENABLE_JACK
576 if (prefs->audio_player == AUD_PLAYER_JACK && mainw->jackd) {
577 if ((*vpp->init_audio)(mainw->jackd->sample_out_rate, mainw->jackd->num_output_channels,
578 vpp->extra_argc, vpp->extra_argv))
579 ext_audio = TRUE;
580 }
581 #endif
582 }
583
584 if (vpp->init_screen) {
585 (*vpp->init_screen)(mainw->vpp->fwidth > 0 ? mainw->vpp->fwidth : mainw->pwidth,
586 mainw->vpp->fheight > 0 ? mainw->vpp->fheight : mainw->pheight,
587 TRUE, xwinid, vpp->extra_argc, vpp->extra_argv);
588 }
589 mainw->ext_audio = ext_audio; // cannot set this until after init_screen()
590 if (mainw->vpp->capabilities & VPP_LOCAL_DISPLAY && prefs->play_monitor == 0) {
591 lives_window_set_keep_below(LIVES_WINDOW(mainw->play_window), TRUE);
592 lives_grab_add(LIVES_MAIN_WINDOW_WIDGET);
593 }
594 } else {
595 mainw->vpp->palette = pal_list[i];
596 if (!(*vpp->set_palette)(vpp->palette)) {
597 do_vpp_palette_error();
598 mainw->error = TRUE;
599 }
600 if (vpp->set_yuv_palette_clamping)(*vpp->set_yuv_palette_clamping)(vpp->YUV_clamping);
601 }
602 break;
603 }
604 }
605 }
606 lives_free(cur_pal);
607 }
608 if (vpp->extra_argv) {
609 for (i = 0; vpp->extra_argv[i]; i++) lives_free(vpp->extra_argv[i]);
610 lives_free(vpp->extra_argv);
611 vpp->extra_argv = NULL;
612 }
613 vpp->extra_argc = 0;
614 if (vppw->rfx) {
615 vpp->extra_argv = param_marshall_to_argv(vppw->rfx);
616 for (i = 0; vpp->extra_argv[i]; vpp->extra_argc = ++i);
617 }
618 mainw->write_vpp_file = TRUE;
619 } else {
620 if (vppw->spinbuttonw)
621 future_prefs->vpp_fwidth = lives_spin_button_get_value_as_int(LIVES_SPIN_BUTTON(vppw->spinbuttonw));
622 else future_prefs->vpp_fwidth = -1;
623 if (vppw->spinbuttonh)
624 future_prefs->vpp_fheight = lives_spin_button_get_value_as_int(LIVES_SPIN_BUTTON(vppw->spinbuttonh));
625 else future_prefs->vpp_fheight = -1;
626 if (vppw->fps_entry) fixed_fps = lives_entry_get_text(LIVES_ENTRY(vppw->fps_entry));
627 if (vppw->pal_entry) {
628 cur_pal = lives_strdup(lives_entry_get_text(LIVES_ENTRY(vppw->pal_entry)));
629
630 if (get_token_count(cur_pal, ' ') > 1) {
631 char **array = lives_strsplit(cur_pal, " ", 2);
632 char *clamping = lives_strdup(array[1] + 1);
633 lives_free(cur_pal);
634 cur_pal = lives_strdup(array[0]);
635 lives_memset(clamping + strlen(clamping) - 1, 0, 1);
636 do {
637 tmp = weed_yuv_clamping_get_name(i);
638 if (tmp && !strcmp(clamping, tmp)) {
639 future_prefs->vpp_YUV_clamping = i;
640 break;
641 }
642 i++;
643 } while (tmp);
644 lives_strfreev(array);
645 lives_free(clamping);
646 }
647 }
648
649 if (fixed_fps) {
650 if (get_token_count((char *)fixed_fps, ':') > 1) {
651 char **array = lives_strsplit(fixed_fps, ":", 2);
652 future_prefs->vpp_fixed_fps_numer = atoi(array[0]);
653 future_prefs->vpp_fixed_fps_denom = atoi(array[1]);
654 lives_strfreev(array);
655 future_prefs->vpp_fixed_fpsd = get_ratio_fps((char *)fixed_fps);
656 } else {
657 future_prefs->vpp_fixed_fpsd = lives_strtod(fixed_fps, NULL);
658 future_prefs->vpp_fixed_fps_numer = 0;
659 }
660 } else {
661 future_prefs->vpp_fixed_fpsd = -1.;
662 future_prefs->vpp_fixed_fps_numer = 0;
663 }
664
665 if (cur_pal) {
666 if (vpp->get_palette_list && (pal_list = (*vpp->get_palette_list)())) {
667 for (i = 0; pal_list[i] != WEED_PALETTE_END; i++) {
668 if (!strcmp(cur_pal, weed_palette_get_name(pal_list[i]))) {
669 future_prefs->vpp_palette = pal_list[i];
670 break;
671 }
672 }
673 }
674 lives_free(cur_pal);
675 } else future_prefs->vpp_palette = WEED_PALETTE_END;
676
677 if (future_prefs->vpp_argv) {
678 for (i = 0; future_prefs->vpp_argv[i]; i++) lives_free(future_prefs->vpp_argv[i]);
679 lives_free(future_prefs->vpp_argv);
680 future_prefs->vpp_argv = NULL;
681 }
682
683 future_prefs->vpp_argc = 0;
684 if (vppw->rfx) {
685 future_prefs->vpp_argv = param_marshall_to_argv(vppw->rfx);
686 if (future_prefs->vpp_argv) {
687 for (i = 0; future_prefs->vpp_argv[i]; future_prefs->vpp_argc = ++i);
688 }
689 } else {
690 future_prefs->vpp_argv = vpp->extra_argv;
691 vpp->extra_argv = NULL;
692 vpp->extra_argc = 0;
693 }
694 }
695 if (button && !mainw->error) on_vppa_cancel_clicked(button, user_data);
696 else lives_dialog_response(LIVES_DIALOG(vppw->dialog), LIVES_RESPONSE_OK);
697 if (button) mainw->error = FALSE;
698 }
699
700
on_vppa_save_clicked(LiVESButton * button,livespointer user_data)701 void on_vppa_save_clicked(LiVESButton *button, livespointer user_data) {
702 _vppaw *vppw = (_vppaw *)user_data;
703 _vid_playback_plugin *vpp = vppw->plugin;
704 char *save_file;
705
706 // apply
707 mainw->error = FALSE;
708 on_vppa_ok_clicked(NULL, user_data);
709 if (mainw->error) {
710 mainw->error = FALSE;
711 return;
712 }
713
714 // get filename
715 save_file = choose_file(NULL, NULL, NULL, LIVES_FILE_CHOOSER_ACTION_SAVE, NULL, NULL);
716 if (!save_file) return;
717
718 // save
719 d_print(_("Saving playback plugin defaults to %s..."), save_file);
720 save_vpp_defaults(vpp, save_file);
721 d_print_done();
722 lives_free(save_file);
723
724 }
725
726
on_vpp_advanced_clicked(LiVESButton * button,livespointer user_data)727 _vppaw *on_vpp_advanced_clicked(LiVESButton *button, livespointer user_data) {
728 LiVESWidget *dialog_vbox;
729 LiVESWidget *hbox;
730 LiVESWidget *label;
731 LiVESWidget *combo;
732 LiVESWidget *cancelbutton;
733 LiVESWidget *okbutton;
734 LiVESWidget *savebutton;
735
736 LiVESAccelGroup *accel_group;
737
738 _vppaw *vppa;
739
740 _vid_playback_plugin *tmpvpp;
741
742 int *pal_list;
743
744 double wscale = 1., hscale = 1.;
745
746 int intention = LIVES_INTENTION_PLAY;
747 int hsize, vsize;
748
749 LiVESList *fps_list_strings = NULL;
750 LiVESList *pal_list_strings = NULL;
751
752 const char *string;
753 const char *pversion;
754 const char *desc;
755 const char *fps_list;
756
757 char *title;
758 char *tmp, *tmp2;
759
760 char *ctext = NULL;
761
762 // TODO - set default values from tmpvpp
763
764 if (user_data) intention = LIVES_POINTER_TO_INT(user_data);
765
766 if (strlen(future_prefs->vpp_name)) {
767 if (!(tmpvpp = open_vid_playback_plugin(future_prefs->vpp_name, FALSE))) return NULL;
768 } else {
769 if (!mainw->vpp) return NULL;
770 tmpvpp = mainw->vpp;
771 }
772
773 vppa = (_vppaw *)(lives_calloc(1, sizeof(_vppaw)));
774
775 vppa->plugin = tmpvpp;
776 /* vppa->rfx = NULL; */
777 /* vppa->spinbuttonh = vppa->spinbuttonw = NULL; */
778 /* vppa->apply_fx = NULL; */
779 /* vppa->pal_entry = vppa->fps_entry = NULL; */
780 /* vppa->keep_rfx = FALSE; */
781
782 vppa->intention = intention;
783
784 pversion = (tmpvpp->version)();
785
786 if (intention == LIVES_INTENTION_PLAY)
787 title = lives_strdup_printf("%s", pversion);
788 else {
789 // LIVES_INTENTION_TRANSCODE
790 title = (_("Quick Transcoding"));
791 wscale = 2. * widget_opts.scale;
792 hscale = 1.5;
793 }
794
795 vppa->dialog = lives_standard_dialog_new(title, FALSE, DEF_DIALOG_WIDTH * wscale, DEF_DIALOG_HEIGHT * hscale);
796 lives_free(title);
797
798 accel_group = LIVES_ACCEL_GROUP(lives_accel_group_new());
799 lives_window_add_accel_group(LIVES_WINDOW(vppa->dialog), accel_group);
800
801 dialog_vbox = lives_dialog_get_content_area(LIVES_DIALOG(vppa->dialog));
802
803 // the filling...
804 if (intention == LIVES_INTENTION_PLAY && tmpvpp->get_description) {
805 desc = (tmpvpp->get_description)();
806 if (desc) {
807 label = lives_standard_label_new(desc);
808 lives_box_pack_start(LIVES_BOX(dialog_vbox), label, FALSE, FALSE, widget_opts.packing_height);
809 }
810 }
811 if (intention == LIVES_INTENTION_TRANSCODE) {
812 tmp = lives_big_and_bold("%s", _("Quick transcode provides a rapid, high quality preview of the selected frames and audio."));
813 widget_opts.use_markup = TRUE;
814 label = lives_standard_label_new(tmp);
815 widget_opts.use_markup = FALSE;
816 lives_free(tmp);
817 lives_box_pack_start(LIVES_BOX(dialog_vbox), label, FALSE, FALSE, widget_opts.packing_height);
818 }
819
820 if (tmpvpp->get_fps_list && (fps_list = (*tmpvpp->get_fps_list)(tmpvpp->palette))) {
821 int nfps, i;
822 char **array = lives_strsplit(fps_list, "|", -1);
823
824 nfps = get_token_count((char *)fps_list, '|');
825 for (i = 0; i < nfps; i++) {
826 if (strlen(array[i]) && strcmp(array[i], "\n")) {
827 if (get_token_count(array[i], ':') == 0) {
828 fps_list_strings = lives_list_append(fps_list_strings, remove_trailing_zeroes(lives_strtod(array[i], NULL)));
829 } else fps_list_strings = lives_list_append(fps_list_strings, lives_strdup(array[i]));
830 }
831 }
832
833 if (intention == LIVES_INTENTION_PLAY) {
834 // fps
835 combo = lives_standard_combo_new((tmp = (_("_FPS"))), fps_list_strings,
836 LIVES_BOX(dialog_vbox), (tmp2 = (_("Fixed framerate for plugin.\n"))));
837 lives_free(tmp);
838 lives_free(tmp2);
839 vppa->fps_entry = lives_combo_get_entry(LIVES_COMBO(combo));
840 lives_entry_set_width_chars(LIVES_ENTRY(lives_combo_get_entry(LIVES_COMBO(combo))), COMBOWIDTHCHARS);
841
842 lives_list_free_all(&fps_list_strings);
843 lives_strfreev(array);
844
845 if (tmpvpp->fixed_fps_numer > 0) {
846 char *tmp = lives_strdup_printf("%d:%d", tmpvpp->fixed_fps_numer, tmpvpp->fixed_fps_denom);
847 lives_entry_set_text(LIVES_ENTRY(vppa->fps_entry), tmp);
848 lives_free(tmp);
849 } else {
850 char *tmp = remove_trailing_zeroes(tmpvpp->fixed_fpsd);
851 lives_entry_set_text(LIVES_ENTRY(vppa->fps_entry), tmp);
852 lives_free(tmp);
853 }
854 }
855 }
856
857 // frame size
858
859 if (!(tmpvpp->capabilities & VPP_LOCAL_DISPLAY)) {
860 hbox = lives_hbox_new(FALSE, 0);
861 lives_box_pack_start(LIVES_BOX(dialog_vbox), hbox, FALSE, FALSE, widget_opts.packing_height);
862
863 add_fill_to_box(LIVES_BOX(hbox));
864
865 hsize = tmpvpp->fwidth > 0 ? tmpvpp->fwidth :
866 intention == LIVES_INTENTION_TRANSCODE ? cfile->hsize : DEF_VPP_HSIZE;
867
868 vppa->spinbuttonw = lives_standard_spin_button_new(_("_Width"),
869 hsize,
870 4., MAX_FRAME_WIDTH, 4., 16., 0, LIVES_BOX(hbox), NULL);
871
872 add_fill_to_box(LIVES_BOX(hbox));
873
874 vsize = tmpvpp->fheight > 0 ? tmpvpp->fheight :
875 intention == LIVES_INTENTION_TRANSCODE ? cfile->vsize : DEF_VPP_VSIZE;
876
877 vppa->spinbuttonh = lives_standard_spin_button_new(_("_Height"),
878 vsize,
879 4., MAX_FRAME_HEIGHT, 4., 16., 0, LIVES_BOX(hbox), NULL);
880
881 if (intention == LIVES_INTENTION_TRANSCODE) {
882 if (mainw->event_list) {
883 lives_widget_set_no_show_all(hbox, TRUE);
884 } else {
885 // add aspect ratio butto
886 lives_special_aspect_t *aspect;
887 /* hbox = lives_hbox_new(FALSE, 0); */
888 /* lives_box_pack_start(LIVES_BOX(dialog_vbox), hbox, FALSE, FALSE, widget_opts.packing_height); */
889 aspect = (lives_special_aspect_t *)add_aspect_ratio_button(LIVES_SPIN_BUTTON(vppa->spinbuttonw),
890 LIVES_SPIN_BUTTON(vppa->spinbuttonh), LIVES_BOX(hbox));
891 // don't reset the aspect params when we make_param_box
892 aspect->no_reset = TRUE;
893 }
894 }
895 add_fill_to_box(LIVES_BOX(hbox));
896 }
897
898 if (intention == LIVES_INTENTION_PLAY) {
899 if (tmpvpp->get_palette_list && (pal_list = (*tmpvpp->get_palette_list)())) {
900 int i;
901
902 for (i = 0; pal_list[i] != WEED_PALETTE_END; i++) {
903 int j = 0;
904 string = weed_palette_get_name(pal_list[i]);
905 if (weed_palette_is_yuv(pal_list[i]) && tmpvpp->get_yuv_palette_clamping) {
906 int *clampings = (*tmpvpp->get_yuv_palette_clamping)(pal_list[i]);
907 while (clampings[j] != -1) {
908 char *string2 = lives_strdup_printf("%s (%s)", string, weed_yuv_clamping_get_name(clampings[j]));
909 pal_list_strings = lives_list_append(pal_list_strings, string2);
910 j++;
911 }
912 }
913 if (j == 0) {
914 pal_list_strings = lives_list_append(pal_list_strings, lives_strdup(string));
915 }
916 }
917
918 combo = lives_standard_combo_new((tmp = (_("_Colourspace"))), pal_list_strings,
919 LIVES_BOX(dialog_vbox), tmp2 = (_("Colourspace input to the plugin.\n")));
920 lives_free(tmp);
921 lives_free(tmp2);
922 vppa->pal_entry = lives_combo_get_entry(LIVES_COMBO(combo));
923
924 if (tmpvpp->get_yuv_palette_clamping && weed_palette_is_yuv(tmpvpp->palette)) {
925 int *clampings = tmpvpp->get_yuv_palette_clamping(tmpvpp->palette);
926 if (clampings[0] != -1)
927 ctext = lives_strdup_printf("%s (%s)", weed_palette_get_name(tmpvpp->palette),
928 weed_yuv_clamping_get_name(tmpvpp->YUV_clamping));
929 }
930 if (!ctext) ctext = lives_strdup(weed_palette_get_name(tmpvpp->palette));
931 lives_entry_set_text(LIVES_ENTRY(vppa->pal_entry), ctext);
932 lives_free(ctext);
933 lives_list_free_all(&pal_list_strings);
934 }
935 }
936 if (intention == LIVES_INTENTION_TRANSCODE) {
937 vppa->apply_fx = lives_standard_check_button_new(_("Apply current realtime effects"),
938 FALSE, LIVES_BOX(dialog_vbox), NULL);
939 if (mainw->event_list) lives_widget_set_no_show_all(widget_opts.last_container, TRUE);
940 }
941
942 // extra params
943 if (tmpvpp->get_init_rfx) {
944 LiVESWidget *vbox = lives_vbox_new(FALSE, 0);
945 /* LiVESWidget *scrolledwindow = lives_standard_scrolled_window_new(RFX_WINSIZE_H, RFX_WINSIZE_V / 2, vbox); */
946 lives_box_pack_start(LIVES_BOX(dialog_vbox), vbox, TRUE, TRUE, 0);
947 plugin_run_param_window((*tmpvpp->get_init_rfx)(intention), LIVES_VBOX(vbox), &(vppa->rfx));
948 if (intention != LIVES_INTENTION_TRANSCODE) {
949 char *fnamex = lives_build_filename(prefs->workdir, vppa->rfx->name, NULL);
950 if (lives_file_test(fnamex, LIVES_FILE_TEST_EXISTS))
951 lives_rm(fnamex);
952 lives_free(fnamex);
953 }
954 if (tmpvpp->extra_argv && tmpvpp->extra_argc > 0) {
955 // update with defaults
956 LiVESList *plist = argv_to_marshalled_list(vppa->rfx, tmpvpp->extra_argc, tmpvpp->extra_argv);
957 param_demarshall(vppa->rfx, plist, FALSE, FALSE); // set defaults
958 param_demarshall(vppa->rfx, plist, FALSE, TRUE); // update widgets
959 lives_list_free_all(&plist);
960 }
961 }
962
963 cancelbutton = lives_dialog_add_button_from_stock(LIVES_DIALOG(vppa->dialog), LIVES_STOCK_CANCEL, NULL,
964 LIVES_RESPONSE_CANCEL);
965
966 lives_widget_add_accelerator(cancelbutton, LIVES_WIDGET_CLICKED_SIGNAL, accel_group,
967 LIVES_KEY_Escape, (LiVESXModifierType)0, (LiVESAccelFlags)0);
968
969 if (intention == LIVES_INTENTION_PLAY) {
970 savebutton = lives_dialog_add_button_from_stock(LIVES_DIALOG(vppa->dialog), LIVES_STOCK_SAVE_AS, NULL,
971 LIVES_RESPONSE_BROWSE);
972
973 lives_widget_set_tooltip_text(savebutton, _("Save settings to an alternate file.\n"));
974 lives_signal_connect(LIVES_GUI_OBJECT(savebutton), LIVES_WIDGET_CLICKED_SIGNAL,
975 LIVES_GUI_CALLBACK(on_vppa_save_clicked),
976 vppa);
977 }
978
979 okbutton = lives_dialog_add_button_from_stock(LIVES_DIALOG(vppa->dialog), LIVES_STOCK_OK, NULL,
980 LIVES_RESPONSE_OK);
981
982 lives_button_grab_default_special(okbutton);
983
984 lives_signal_sync_connect(LIVES_GUI_OBJECT(cancelbutton), LIVES_WIDGET_CLICKED_SIGNAL,
985 LIVES_GUI_CALLBACK(on_vppa_cancel_clicked), vppa);
986
987 lives_signal_sync_connect(LIVES_GUI_OBJECT(okbutton), LIVES_WIDGET_CLICKED_SIGNAL,
988 LIVES_GUI_CALLBACK(on_vppa_ok_clicked), vppa);
989
990 lives_widget_show_all(vppa->dialog);
991 lives_window_present(LIVES_WINDOW(vppa->dialog));
992 lives_xwindow_raise(lives_widget_get_xwindow(vppa->dialog));
993
994 return vppa;
995 }
996
997
close_vid_playback_plugin(_vid_playback_plugin * vpp)998 void close_vid_playback_plugin(_vid_playback_plugin *vpp) {
999 register int i;
1000
1001 if (vpp) {
1002 if (vpp == mainw->vpp) {
1003 lives_grab_remove(LIVES_MAIN_WINDOW_WIDGET);
1004 if (mainw->ext_playback) {
1005 pthread_mutex_lock(&mainw->vpp_stream_mutex);
1006 mainw->ext_audio = FALSE;
1007 mainw->ext_playback = FALSE;
1008 pthread_mutex_unlock(&mainw->vpp_stream_mutex);
1009 if (mainw->vpp->exit_screen) {
1010 (*mainw->vpp->exit_screen)(mainw->ptr_x, mainw->ptr_y);
1011 }
1012 #ifdef RT_AUDIO
1013 stop_audio_stream();
1014 #endif
1015 if (mainw->vpp->capabilities & VPP_LOCAL_DISPLAY)
1016 if (mainw->play_window && prefs->play_monitor == 0)
1017 lives_window_set_keep_below(LIVES_WINDOW(mainw->play_window), FALSE);
1018 }
1019 mainw->stream_ticks = -1;
1020 mainw->vpp = NULL;
1021 }
1022 if (vpp->module_unload)(vpp->module_unload)();
1023 dlclose(vpp->handle);
1024
1025 if (vpp->extra_argv) {
1026 for (i = 0; vpp->extra_argv[i]; i++) {
1027 lives_free(vpp->extra_argv[i]);
1028 }
1029 lives_free(vpp->extra_argv);
1030 }
1031
1032 for (i = 0; i < vpp->num_play_params + vpp->num_alpha_chans; i++) {
1033 weed_plant_free(vpp->play_params[i]);
1034 }
1035
1036 lives_freep((void **)&vpp->play_params);
1037 lives_free(vpp);
1038 }
1039 }
1040
1041
pp_get_param(weed_plant_t ** pparams,int idx)1042 const weed_plant_t *pp_get_param(weed_plant_t **pparams, int idx) {
1043 register int i = 0;
1044 while (pparams[i]) {
1045 if (WEED_PLANT_IS_PARAMETER(pparams[i])) {
1046 if (--idx < 0) return pparams[i];
1047 }
1048 i++;
1049 }
1050 return NULL;
1051 }
1052
1053
pp_get_chan(weed_plant_t ** pparams,int idx)1054 const weed_plant_t *pp_get_chan(weed_plant_t **pparams, int idx) {
1055 register int i = 0;
1056 while (pparams[i]) {
1057 if (WEED_PLANT_IS_CHANNEL(pparams[i])) {
1058 if (--idx < 0) return pparams[i];
1059 }
1060 i++;
1061 }
1062 return NULL;
1063 }
1064
1065
vpp_try_match_palette(_vid_playback_plugin * vpp,weed_layer_t * layer)1066 boolean vpp_try_match_palette(_vid_playback_plugin *vpp, weed_layer_t *layer) {
1067 int *pal_list, i = 0;
1068 if (vpp->get_palette_list && (pal_list = (*vpp->get_palette_list)())) {
1069 int palette = weed_layer_get_palette(layer);
1070 for (i = 0; pal_list[i] != WEED_PALETTE_END; i++) {
1071 if (pal_list[i] == palette) break;
1072 }
1073 if (pal_list[i] == WEED_PALETTE_END) {
1074 if (!i) return FALSE;
1075 palette = best_palette_match(pal_list, i, palette);
1076 }
1077 if (palette == WEED_PALETTE_END) return FALSE;
1078 if (palette != vpp->palette) {
1079 if (!(*vpp->set_palette)(palette)) {
1080 return FALSE;
1081 }
1082 }
1083 vpp->palette = palette;
1084 if (weed_palette_is_yuv(palette) && vpp->get_yuv_palette_clamping) {
1085 int *yuv_clamping_types = (*vpp->get_yuv_palette_clamping)(vpp->palette);
1086 int lclamping = weed_layer_get_yuv_clamping(layer);
1087 for (i = 0; yuv_clamping_types[i] != -1; i++) {
1088 if (yuv_clamping_types[i] == lclamping) {
1089 if ((*vpp->set_yuv_palette_clamping)(lclamping))
1090 vpp->YUV_clamping = lclamping;
1091 break;
1092 // *INDENT-OFF*
1093 }}}}
1094 // *INDENT-ON*
1095 return FALSE;
1096 }
1097
1098
open_vid_playback_plugin(const char * name,boolean in_use)1099 _vid_playback_plugin *open_vid_playback_plugin(const char *name, boolean in_use) {
1100 // this is called on startup or when the user selects a new playback plugin
1101
1102 // if in_use is TRUE, it is our active vpp
1103
1104 // TODO - if in_use, get fixed_fps,fwidth,fheight,palette,argc and argv from a file
1105
1106 _vid_playback_plugin *vpp;
1107 char **array;
1108 const char *fps_list;
1109 const char *pl_error;
1110 void *handle;
1111 int *palette_list;
1112 char *msg, *tmp;
1113 char *plugname;
1114 int dlflags = RTLD_LAZY;
1115 boolean OK = TRUE;
1116
1117 if (in_use && LIVES_IS_PLAYING && mainw->noswitch) {
1118 mainw->new_vpp = name;
1119 return NULL;
1120 }
1121
1122 tmp = lives_strdup_printf("%s.%s", name, DLL_NAME);
1123 plugname = lives_build_filename(prefs->lib_dir, PLUGIN_EXEC_DIR, PLUGIN_VID_PLAYBACK, tmp, NULL);
1124 lives_free(tmp);
1125
1126 handle = dlopen(plugname, dlflags);
1127
1128 if (!handle) {
1129 char *msg = lives_strdup_printf(_("\n\nFailed to open playback plugin %s\nError was %s\n"
1130 "Playback plugin will be disabled,\n"
1131 "it can be re-enabled in Preferences / Playback.\n"), plugname, dlerror());
1132 if (prefs->startup_phase != 1 && prefs->startup_phase != -1) {
1133 if (!prefsw) widget_opts.non_modal = TRUE;
1134 do_error_dialog(msg);
1135 widget_opts.non_modal = FALSE;
1136 }
1137 LIVES_ERROR(msg);
1138 lives_free(msg);
1139 lives_free(plugname);
1140 lives_snprintf(future_prefs->vpp_name, 64, "%s", mainw->string_constants[LIVES_STRING_CONSTANT_NONE]);
1141 set_vpp(TRUE);
1142 return NULL;
1143 }
1144
1145 vpp = (_vid_playback_plugin *) lives_calloc(sizeof(_vid_playback_plugin), 1);
1146
1147 vpp->play_paramtmpls = NULL;
1148 vpp->get_init_rfx = NULL;
1149 vpp->play_params = NULL;
1150 vpp->alpha_chans = NULL;
1151 vpp->num_play_params = vpp->num_alpha_chans = 0;
1152 vpp->extra_argv = NULL;
1153
1154 if ((vpp->module_check_init = (const char *(*)())dlsym(handle, "module_check_init")) == NULL) {
1155 OK = FALSE;
1156 }
1157 if ((vpp->version = (const char *(*)())dlsym(handle, "version")) == NULL) {
1158 OK = FALSE;
1159 }
1160 if ((vpp->get_palette_list = (int *(*)())dlsym(handle, "get_palette_list")) == NULL) {
1161 OK = FALSE;
1162 }
1163 if ((vpp->set_palette = (boolean(*)(int))dlsym(handle, "set_palette")) == NULL) {
1164 OK = FALSE;
1165 }
1166 if ((vpp->get_capabilities = (uint64_t (*)(int))dlsym(handle, "get_capabilities")) == NULL) {
1167 OK = FALSE;
1168 }
1169 if ((vpp->render_frame = (boolean(*)(int, int, int64_t, void **, void **, weed_plant_t **))
1170 dlsym(handle, "render_frame")) == NULL) {
1171 if ((vpp->play_frame = (boolean(*)(weed_layer_t *, ticks_t, weed_layer_t *))
1172 dlsym(handle, "play_frame")) == NULL) {
1173 OK = FALSE;
1174 }
1175 }
1176 if ((vpp->get_fps_list = (const char *(*)(int))dlsym(handle, "get_fps_list"))) {
1177 if ((vpp->set_fps = (boolean(*)(double))dlsym(handle, "set_fps")) == NULL) {
1178 OK = FALSE;
1179 }
1180 }
1181
1182 if (!OK) {
1183 char *msg = lives_strdup_printf
1184 (_("\n\nPlayback module %s\nis missing a mandatory function.\nUnable to use it.\n"), plugname);
1185 set_string_pref(PREF_VID_PLAYBACK_PLUGIN, "none");
1186 do_error_dialog(msg);
1187 lives_free(msg);
1188 dlclose(handle);
1189 lives_free(vpp);
1190 vpp = NULL;
1191 lives_free(plugname);
1192 return NULL;
1193 }
1194
1195 if ((pl_error = (*vpp->module_check_init)())) {
1196 msg = lives_strdup_printf(_("Video playback plugin failed to initialise.\nError was: %s\n"), pl_error);
1197 if (prefs->startup_phase != 1 && prefs->startup_phase != -1) {
1198 do_error_dialog(msg);
1199 } else {
1200 LIVES_ERROR(msg);
1201 }
1202 lives_free(msg);
1203 dlclose(handle);
1204 lives_free(vpp);
1205 vpp = NULL;
1206 lives_free(plugname);
1207 return NULL;
1208 }
1209
1210 // now check for optional functions
1211 vpp->weed_setup = (weed_plant_t *(*)(weed_bootstrap_f))dlsym(handle, "weed_setup");
1212
1213 if (vpp->weed_setup) {
1214 weed_set_host_info_callback(host_info_cb, LIVES_INT_TO_POINTER(100));
1215 (*vpp->weed_setup)(weed_bootstrap);
1216 }
1217
1218 vpp->get_description = (const char *(*)())dlsym(handle, "get_description");
1219 vpp->get_init_rfx = (const char *(*)())dlsym(handle, "get_init_rfx");
1220
1221 vpp->get_play_params = (const weed_plant_t **(*)(weed_bootstrap_f))dlsym(handle, "get_play_params");
1222
1223 vpp->get_yuv_palette_clamping = (int *(*)(int))dlsym(handle, "get_yuv_palette_clamping");
1224 vpp->set_yuv_palette_clamping = (int (*)(int))dlsym(handle, "set_yuv_palette_clamping");
1225 vpp->get_audio_fmts = (int *(*)())dlsym(handle, "get_audio_fmts");
1226 vpp->init_screen = (boolean(*)(int, int, boolean, uint64_t, int, char **))dlsym(handle, "init_screen");
1227 vpp->init_audio = (boolean(*)(int, int, int, char **))dlsym(handle, "init_audio");
1228 vpp->render_audio_frame_float = (boolean(*)(float **, int))dlsym(handle, "render_audio_frame_float");
1229 vpp->exit_screen = (void (*)(uint16_t, uint16_t))dlsym(handle, "exit_screen");
1230 vpp->module_unload = (void (*)())dlsym(handle, "module_unload");
1231
1232 vpp->YUV_sampling = 0;
1233 vpp->YUV_subspace = 0;
1234
1235 palette_list = (*vpp->get_palette_list)();
1236
1237 if (future_prefs->vpp_argv) {
1238 vpp->palette = future_prefs->vpp_palette;
1239 vpp->YUV_clamping = future_prefs->vpp_YUV_clamping;
1240 } else {
1241 if (!in_use && mainw->vpp && !(strcmp(name, mainw->vpp->name))) {
1242 vpp->palette = mainw->vpp->palette;
1243 vpp->YUV_clamping = mainw->vpp->YUV_clamping;
1244 } else {
1245 vpp->palette = palette_list[0];
1246 vpp->YUV_clamping = -1;
1247 }
1248 }
1249
1250 vpp->audio_codec = AUDIO_CODEC_NONE;
1251 vpp->capabilities = (*vpp->get_capabilities)(vpp->palette);
1252
1253 if (vpp->capabilities & VPP_CAN_RESIZE && vpp->capabilities & VPP_LOCAL_DISPLAY) {
1254 vpp->fwidth = vpp->fheight = -1;
1255 } else {
1256 vpp->fwidth = vpp->fheight = 0;
1257 }
1258 if (future_prefs->vpp_argv) {
1259 vpp->fwidth = future_prefs->vpp_fwidth;
1260 vpp->fheight = future_prefs->vpp_fheight;
1261 } else if (!in_use && mainw->vpp && !(strcmp(name, mainw->vpp->name))) {
1262 vpp->fwidth = mainw->vpp->fwidth;
1263 vpp->fheight = mainw->vpp->fheight;
1264 }
1265 if (vpp->fwidth == -1 && !(vpp->capabilities & VPP_CAN_RESIZE && vpp->capabilities & VPP_LOCAL_DISPLAY)) {
1266 vpp->fwidth = vpp->fheight = 0;
1267 }
1268
1269 vpp->fixed_fpsd = -1.;
1270 vpp->fixed_fps_numer = 0;
1271
1272 if (future_prefs->vpp_argv) {
1273 vpp->fixed_fpsd = future_prefs->vpp_fixed_fpsd;
1274 vpp->fixed_fps_numer = future_prefs->vpp_fixed_fps_numer;
1275 vpp->fixed_fps_denom = future_prefs->vpp_fixed_fps_denom;
1276 } else if (!in_use && mainw->vpp && !(strcmp(name, mainw->vpp->name))) {
1277 vpp->fixed_fpsd = mainw->vpp->fixed_fpsd;
1278 vpp->fixed_fps_numer = mainw->vpp->fixed_fps_numer;
1279 vpp->fixed_fps_denom = mainw->vpp->fixed_fps_denom;
1280 }
1281
1282 vpp->handle = handle;
1283 lives_snprintf(vpp->name, 256, "%s", name);
1284
1285 if (future_prefs->vpp_argv) {
1286 vpp->extra_argc = future_prefs->vpp_argc;
1287 vpp->extra_argv = (char **)lives_calloc((vpp->extra_argc + 1), (sizeof(char *)));
1288 for (register int i = 0; i <= vpp->extra_argc; i++) vpp->extra_argv[i] = lives_strdup(future_prefs->vpp_argv[i]);
1289 } else {
1290 if (!in_use && mainw->vpp && !(strcmp(name, mainw->vpp->name))) {
1291 vpp->extra_argc = mainw->vpp->extra_argc;
1292 vpp->extra_argv = (char **)lives_calloc((mainw->vpp->extra_argc + 1), (sizeof(char *)));
1293 for (register int i = 0; i <= vpp->extra_argc; i++) vpp->extra_argv[i] = lives_strdup(mainw->vpp->extra_argv[i]);
1294 } else {
1295 vpp->extra_argc = 0;
1296 vpp->extra_argv = (char **)lives_malloc(sizeof(char *));
1297 vpp->extra_argv[0] = NULL;
1298 }
1299 }
1300 // see if plugin is using fixed fps
1301
1302 if (vpp->fixed_fpsd <= 0. && vpp->get_fps_list) {
1303 // fixed fps
1304
1305 if ((fps_list = (*vpp->get_fps_list)(vpp->palette))) {
1306 array = lives_strsplit(fps_list, "|", -1);
1307 if (get_token_count(array[0], ':') > 1) {
1308 char **array2 = lives_strsplit(array[0], ":", 2);
1309 vpp->fixed_fps_numer = atoi(array2[0]);
1310 vpp->fixed_fps_denom = atoi(array2[1]);
1311 lives_strfreev(array2);
1312 vpp->fixed_fpsd = get_ratio_fps(array[0]);
1313 } else {
1314 vpp->fixed_fpsd = lives_strtod(array[0], NULL);
1315 vpp->fixed_fps_numer = 0;
1316 }
1317 lives_strfreev(array);
1318 }
1319 }
1320
1321 if (vpp->YUV_clamping == -1) {
1322 vpp->YUV_clamping = WEED_YUV_CLAMPING_CLAMPED;
1323
1324 if (vpp->get_yuv_palette_clamping && weed_palette_is_yuv(vpp->palette)) {
1325 int *yuv_clamping_types = (*vpp->get_yuv_palette_clamping)(vpp->palette);
1326 if (yuv_clamping_types[0] != -1) vpp->YUV_clamping = yuv_clamping_types[0];
1327 }
1328 }
1329
1330 if (vpp->get_audio_fmts && mainw->is_ready) vpp->audio_codec = get_best_audio(vpp);
1331 if (prefsw) {
1332 prefsw_set_astream_settings(vpp, prefsw);
1333 prefsw_set_rec_after_settings(vpp, prefsw);
1334 }
1335
1336 /// get the play parameters (and alpha channels) if any and convert to weed params
1337 if (vpp->get_play_params) {
1338 vpp->play_paramtmpls = (*vpp->get_play_params)(NULL);
1339 }
1340
1341 // create vpp->play_params
1342 if (vpp->play_paramtmpls) {
1343 register int i;
1344 weed_plant_t *ptmpl;
1345 for (i = 0; (ptmpl = (weed_plant_t *)vpp->play_paramtmpls[i]); i++) {
1346 vpp->play_params = (weed_plant_t **)lives_realloc(vpp->play_params, (i + 2) * sizeof(weed_plant_t *));
1347 if (WEED_PLANT_IS_PARAMETER_TEMPLATE(ptmpl)) {
1348 // is param template, create a param
1349 vpp->play_params[i] = weed_plant_new(WEED_PLANT_PARAMETER);
1350 weed_leaf_copy(vpp->play_params[i], WEED_LEAF_VALUE, ptmpl, WEED_LEAF_DEFAULT);
1351 weed_set_plantptr_value(vpp->play_params[i], WEED_LEAF_TEMPLATE, ptmpl);
1352 vpp->num_play_params++;
1353 } else {
1354 // must be an alpha channel
1355 vpp->play_params[i] = weed_plant_new(WEED_PLANT_CHANNEL);
1356 weed_set_plantptr_value(vpp->play_params[i], WEED_LEAF_TEMPLATE, ptmpl);
1357 vpp->num_alpha_chans++;
1358 }
1359 }
1360 vpp->play_params[i] = NULL;
1361 }
1362
1363 if (!in_use) return vpp;
1364
1365 if (!mainw->is_ready) {
1366 double fixed_fpsd = vpp->fixed_fpsd;
1367 int fwidth = vpp->fwidth;
1368 int fheight = vpp->fheight;
1369
1370 mainw->vpp = vpp;
1371 load_vpp_defaults(vpp, mainw->vpp_defs_file);
1372 if (fixed_fpsd < 0.) vpp->fixed_fpsd = fixed_fpsd;
1373 if (fwidth < 0) vpp->fwidth = fwidth;
1374 if (fheight < 0) vpp->fheight = fheight;
1375 }
1376
1377 if (!(*vpp->set_palette)(vpp->palette)) {
1378 do_vpp_palette_error();
1379 close_vid_playback_plugin(vpp);
1380 lives_free(plugname);
1381 return NULL;
1382 }
1383
1384 if (vpp->set_yuv_palette_clamping)(*vpp->set_yuv_palette_clamping)(vpp->YUV_clamping);
1385
1386 if (vpp->get_fps_list) {
1387 if (mainw->fixed_fpsd > 0. || (vpp->fixed_fpsd > 0. && vpp->set_fps &&
1388 !((*vpp->set_fps)(vpp->fixed_fpsd)))) {
1389 do_vpp_fps_error();
1390 vpp->fixed_fpsd = -1.;
1391 vpp->fixed_fps_numer = 0;
1392 }
1393 }
1394
1395 cached_key = cached_mod = 0;
1396
1397 // TODO: - support other YUV subspaces
1398 d_print(_("*** Using %s plugin for fs playback, agreed to use palette type %d ( %s ). ***\n"), name,
1399 vpp->palette, (tmp = weed_palette_get_name_full(vpp->palette, vpp->YUV_clamping,
1400 WEED_YUV_SUBSPACE_YCBCR)));
1401 lives_free(tmp);
1402
1403 lives_free(plugname);
1404
1405 if (mainw->is_ready && in_use && mainw->vpp) {
1406 close_vid_playback_plugin(mainw->vpp);
1407 }
1408
1409 return vpp;
1410 }
1411
1412
vid_playback_plugin_exit(void)1413 void vid_playback_plugin_exit(void) {
1414 // external plugin
1415 if (mainw->ext_playback) {
1416 pthread_mutex_lock(&mainw->vpp_stream_mutex);
1417 mainw->ext_audio = FALSE;
1418 pthread_mutex_unlock(&mainw->vpp_stream_mutex);
1419 lives_grab_remove(LIVES_MAIN_WINDOW_WIDGET);
1420
1421 if (mainw->vpp->exit_screen) {
1422 (*mainw->vpp->exit_screen)(mainw->ptr_x, mainw->ptr_y);
1423 }
1424 #ifdef RT_AUDIO
1425 stop_audio_stream();
1426 #endif
1427 mainw->ext_playback = FALSE;
1428 if (mainw->vpp->capabilities & VPP_LOCAL_DISPLAY)
1429 if (mainw->play_window && prefs->play_monitor == 0)
1430 lives_window_set_keep_below(LIVES_WINDOW(mainw->play_window), FALSE);
1431 }
1432 mainw->stream_ticks = -1;
1433
1434 if (LIVES_IS_PLAYING && mainw->fs && mainw->sep_win) lives_window_fullscreen(LIVES_WINDOW(mainw->play_window));
1435 if (mainw->play_window) {
1436 play_window_set_title();
1437 }
1438 }
1439
1440
get_best_audio(_vid_playback_plugin * vpp)1441 int64_t get_best_audio(_vid_playback_plugin * vpp) {
1442 // find best audio from video plugin list, matching with audiostream plugins
1443
1444 // i.e. cross-check video list with astreamer list
1445
1446 // only for plugins which want to stream audiom but dont provide a render_audio_frame()
1447
1448 int *fmts, *sfmts;
1449 int ret = AUDIO_CODEC_NONE;
1450 int i, j = 0, nfmts;
1451 char *astreamer, *com;
1452 char buf[1024];
1453 char **array;
1454
1455 if (vpp && vpp->get_audio_fmts) {
1456 fmts = (*vpp->get_audio_fmts)(); // const, so do not free()
1457
1458 // make audiostream plugin name
1459 astreamer = lives_build_filename(prefs->lib_dir, PLUGIN_EXEC_DIR, PLUGIN_AUDIO_STREAM, AUDIO_STREAMER_NAME, NULL);
1460
1461 // create sfmts array and nfmts
1462
1463 com = lives_strdup_printf("\"%s\" get_formats", astreamer);
1464 lives_popen(com, FALSE, buf, 1024);
1465 lives_free(com);
1466
1467 nfmts = get_token_count(buf, '|');
1468 array = lives_strsplit(buf, "|", nfmts);
1469 sfmts = (int *)lives_calloc(nfmts, sizint);
1470
1471 for (i = 0; i < nfmts; i++) {
1472 if (array[i] && *array[i]) sfmts[j++] = atoi(array[i]);
1473 }
1474
1475 nfmts = j;
1476 lives_strfreev(array);
1477
1478 for (i = 0; fmts[i] != -1; i++) {
1479 // traverse video list and see if audiostreamer supports each one
1480 if (int_array_contains_value(sfmts, nfmts, fmts[i])) {
1481
1482 com = lives_strdup_printf("\"%s\" check %d", astreamer, fmts[i]);
1483 lives_popen(com, FALSE, buf, 1024);
1484 lives_free(com);
1485
1486 if (THREADVAR(com_failed)) {
1487 lives_free(astreamer);
1488 lives_free(com);
1489 lives_free(sfmts);
1490 return ret;
1491 }
1492
1493 if (*buf) {
1494 if (i == 0 && prefsw) {
1495 do_error_dialog(buf);
1496 d_print(_("Audio stream unable to use preferred format '%s'\n"), anames[fmts[i]]);
1497 }
1498 continue;
1499 }
1500
1501 if (i > 0 && prefsw) {
1502 d_print(_("Using format '%s' instead.\n"), anames[fmts[i]]);
1503 }
1504 ret = fmts[i];
1505 break;
1506 }
1507 }
1508
1509 if (fmts[i] == -1) {
1510 //none suitable, stick with first
1511 for (i = 0; fmts[i] != -1; i++) {
1512 // traverse video list and see if audiostreamer supports each one
1513 if (int_array_contains_value(sfmts, nfmts, fmts[i])) {
1514 ret = fmts[i];
1515 break;
1516 }
1517 }
1518 }
1519
1520 lives_free(sfmts);
1521 lives_free(astreamer);
1522 }
1523
1524 return ret;
1525 }
1526
1527
1528 ///////////////////////
1529 // encoder plugins
1530
do_plugin_encoder_error(const char * plugin_name)1531 void do_plugin_encoder_error(const char *plugin_name) {
1532 char *msg, *tmp;
1533
1534 if (!plugin_name) {
1535 msg = lives_strdup_printf(
1536 _("LiVES was unable to find its encoder plugins. Please make sure you have the plugins installed in\n"
1537 "%s%s%s\nor change the value of <lib_dir> in %s\n"),
1538 prefs->lib_dir, PLUGIN_EXEC_DIR, PLUGIN_ENCODERS,
1539 (tmp = lives_filename_to_utf8(prefs->configfile, -1, NULL, NULL, NULL)));
1540 lives_free(tmp);
1541 widget_opts.non_modal = TRUE;
1542 do_error_dialog(msg);
1543 lives_free(msg);
1544 return;
1545 }
1546
1547 msg = lives_strdup_printf(
1548 _("LiVES did not receive a response from the encoder plugin called '%s'.\n"
1549 "Please make sure you have that plugin installed correctly in\n%s%s%s\n"
1550 "or switch to another plugin using Tools|Preferences|Encoding\n"),
1551 plugin_name, prefs->lib_dir, PLUGIN_EXEC_DIR, PLUGIN_ENCODERS);
1552 do_error_dialog(msg);
1553 lives_free(msg);
1554 }
1555
1556
check_encoder_restrictions(boolean get_extension,boolean user_audio,boolean save_all)1557 boolean check_encoder_restrictions(boolean get_extension, boolean user_audio, boolean save_all) {
1558 LiVESList *ofmt_all = NULL;
1559 char **checks;
1560 char **array = NULL;
1561 char **array2;
1562 char aspect_buffer[512];
1563
1564 // for auto resizing/resampling
1565 double best_fps = 0.;
1566 double fps;
1567 double best_fps_delta = 0.;
1568
1569 boolean sizer = FALSE;
1570 boolean allow_aspect_override = FALSE;
1571 boolean calc_aspect = FALSE;
1572 boolean swap_endian = FALSE;
1573
1574 int best_arate = 0;
1575 int width, owidth;
1576 int height, oheight;
1577 int best_arate_delta = 0;
1578 int hblock = 2, vblock = 2;
1579 int i, r, val;
1580 int pieces, numtok;
1581 int best_fps_num = 0, best_fps_denom = 0;
1582 int arate, achans, asampsize, asigned = 0;
1583
1584 if (!rdet) {
1585 width = owidth = cfile->hsize;
1586 height = oheight = cfile->vsize;
1587 fps = cfile->fps;
1588 } else {
1589 width = owidth = rdet->width;
1590 height = oheight = rdet->height;
1591 fps = rdet->fps;
1592 rdet->suggestion_followed = FALSE;
1593 }
1594
1595 if (mainw->osc_auto) {
1596 if (mainw->osc_enc_width > 0) {
1597 width = mainw->osc_enc_width;
1598 height = mainw->osc_enc_height;
1599 }
1600 if (mainw->osc_enc_fps != 0.) fps = mainw->osc_enc_fps;
1601 }
1602
1603 // TODO - allow lists for size
1604 lives_snprintf(prefs->encoder.of_restrict, 5, "none");
1605 if ((ofmt_all = plugin_request_by_line(PLUGIN_ENCODERS, prefs->encoder.name, "get_formats"))) {
1606 // get any restrictions for the current format
1607 for (i = 0; i < lives_list_length(ofmt_all); i++) {
1608 if ((numtok = get_token_count((char *)lives_list_nth_data(ofmt_all, i), '|')) > 2) {
1609 array = lives_strsplit((char *)lives_list_nth_data(ofmt_all, i), "|", -1);
1610 if (!strcmp(array[0], prefs->encoder.of_name)) {
1611 if (numtok > 5) {
1612 lives_snprintf(prefs->encoder.ptext, 512, "%s", array[5]);
1613 } else {
1614 lives_memset(prefs->encoder.ptext, 0, 1);
1615 }
1616 if (numtok > 4) {
1617 lives_snprintf(prefs->encoder.of_def_ext, 16, "%s", array[4]);
1618 } else {
1619 lives_memset(prefs->encoder.of_def_ext, 0, 1);
1620 }
1621 if (numtok > 3) {
1622 lives_snprintf(prefs->encoder.of_restrict, 128, "%s", array[3]);
1623 } else {
1624 lives_snprintf(prefs->encoder.of_restrict, 128, "none");
1625 }
1626 prefs->encoder.of_allowed_acodecs = atoi(array[2]);
1627 lives_list_free_all(&ofmt_all);
1628 lives_strfreev(array);
1629 break;
1630 }
1631 lives_strfreev(array);
1632 }
1633 }
1634 }
1635
1636 if (get_extension) {
1637 return TRUE; // just wanted file extension
1638 }
1639
1640 if (!rdet && mainw->save_with_sound && prefs->encoder.audio_codec != AUDIO_CODEC_NONE) {
1641 if (!(prefs->encoder.of_allowed_acodecs & (1 << prefs->encoder.audio_codec))) {
1642 do_encoder_acodec_error();
1643 return FALSE;
1644 }
1645 }
1646
1647 if (user_audio && future_prefs->encoder.of_allowed_acodecs == 0) best_arate = -1;
1648
1649 if ((!*prefs->encoder.of_restrict || !strcmp(prefs->encoder.of_restrict, "none")) && best_arate > -1) {
1650 return TRUE;
1651 }
1652
1653 if (!rdet) {
1654 arate = cfile->arate;
1655 achans = cfile->achans;
1656 asampsize = cfile->asampsize;
1657 } else {
1658 arate = rdet->arate;
1659 achans = rdet->achans;
1660 asampsize = rdet->asamps;
1661 }
1662
1663 // audio endianness check - what should we do for big-endian machines ?
1664 if (((mainw->save_with_sound || rdet) && (!resaudw || resaudw->aud_checkbutton ||
1665 lives_toggle_button_get_active(LIVES_TOGGLE_BUTTON(resaudw->aud_checkbutton))))
1666 && prefs->encoder.audio_codec != AUDIO_CODEC_NONE && arate != 0 && achans != 0 && asampsize != 0) {
1667 if (rdet && !rdet->is_encoding) {
1668 if (mainw->endian != AFORM_BIG_ENDIAN && (lives_toggle_button_get_active(LIVES_TOGGLE_BUTTON(resaudw->rb_bigend))))
1669 swap_endian = TRUE;
1670 } else {
1671 if (mainw->endian != AFORM_BIG_ENDIAN && (cfile->signed_endian & AFORM_BIG_ENDIAN)) swap_endian = TRUE;
1672 //if (mainw->endian==AFORM_BIG_ENDIAN && (cfile->signed_endian&AFORM_BIG_ENDIAN)) swap_endian=TRUE; // needs test
1673 }
1674 }
1675
1676 if (*prefs->encoder.of_restrict) {
1677 pieces = get_token_count(prefs->encoder.of_restrict, ',');
1678 checks = lives_strsplit(prefs->encoder.of_restrict, ",", pieces);
1679
1680 for (r = 0; r < pieces; r++) {
1681 // check each restriction in turn
1682
1683 if (!strncmp(checks[r], "fps=", 4)) {
1684 double allowed_fps;
1685 int mbest_num = 0, mbest_denom = 0;
1686 int numparts;
1687 char *fixer;
1688
1689 best_fps_delta = 1000000000.;
1690 array = lives_strsplit(checks[r], "=", 2);
1691 numtok = get_token_count(array[1], ';');
1692 array2 = lives_strsplit(array[1], ";", numtok);
1693 for (i = 0; i < numtok; i++) {
1694 mbest_num = mbest_denom = 0;
1695 if ((numparts = get_token_count(array2[i], ':')) > 1) {
1696 char **array3 = lives_strsplit(array2[i], ":", 2);
1697 mbest_num = atoi(array3[0]);
1698 mbest_denom = atoi(array3[1]);
1699 lives_strfreev(array3);
1700 if (mbest_denom == 0) continue;
1701 allowed_fps = (mbest_num * 1.) / (mbest_denom * 1.);
1702 } else allowed_fps = lives_strtod(array2[i], NULL);
1703
1704 // convert to 8dp
1705 fixer = lives_strdup_printf("%.8f %.8f", allowed_fps, fps);
1706 lives_free(fixer);
1707
1708 if (allowed_fps >= fps) {
1709 if (allowed_fps - fps < best_fps_delta) {
1710 best_fps_delta = allowed_fps - fps;
1711 if (mbest_denom > 0) {
1712 best_fps_num = mbest_num;
1713 best_fps_denom = mbest_denom;
1714 best_fps = 0.;
1715 if (!rdet) cfile->ratio_fps = TRUE;
1716 else rdet->ratio_fps = TRUE;
1717 } else {
1718 best_fps_num = best_fps_denom = 0;
1719 best_fps = allowed_fps;
1720 if (!rdet) cfile->ratio_fps = FALSE;
1721 else rdet->ratio_fps = FALSE;
1722 }
1723 }
1724 } else if ((best_fps_denom == 0 && allowed_fps > best_fps) || (best_fps_denom > 0
1725 && allowed_fps > (best_fps_num * 1.) /
1726 (best_fps_denom * 1.))) {
1727 best_fps_delta = fps - allowed_fps;
1728 if (mbest_denom > 0) {
1729 best_fps_num = mbest_num;
1730 best_fps_denom = mbest_denom;
1731 best_fps = 0.;
1732 if (!rdet) cfile->ratio_fps = TRUE;
1733 else rdet->ratio_fps = TRUE;
1734 } else {
1735 best_fps = allowed_fps;
1736 best_fps_num = best_fps_denom = 0;
1737 if (!rdet) cfile->ratio_fps = FALSE;
1738 else rdet->ratio_fps = FALSE;
1739 }
1740 }
1741 if (best_fps_delta <= prefs->fps_tolerance) {
1742 best_fps_delta = 0.;
1743 best_fps_denom = best_fps_num = 0;
1744 }
1745 if (best_fps_delta == 0.) break;
1746 }
1747 lives_strfreev(array);
1748 lives_strfreev(array2);
1749 continue;
1750 }
1751
1752 if (!strncmp(checks[r], "size=", 5)) {
1753 // TODO - allow list for size
1754 array = lives_strsplit(checks[r], "=", 2);
1755 array2 = lives_strsplit(array[1], "x", 2);
1756 width = atoi(array2[0]);
1757 height = atoi(array2[1]);
1758 lives_strfreev(array2);
1759 lives_strfreev(array);
1760 sizer = TRUE;
1761 continue;
1762 }
1763
1764 if (!strncmp(checks[r], "minw=", 5)) {
1765 array = lives_strsplit(checks[r], "=", 2);
1766 val = atoi(array[1]);
1767 if (width < val) width = val;
1768 lives_strfreev(array);
1769 continue;
1770 }
1771
1772 if (!strncmp(checks[r], "minh=", 5)) {
1773 array = lives_strsplit(checks[r], "=", 2);
1774 val = atoi(array[1]);
1775 if (height < val) height = val;
1776 lives_strfreev(array);
1777 continue;
1778 }
1779
1780 if (!strncmp(checks[r], "maxh=", 5)) {
1781 array = lives_strsplit(checks[r], "=", 2);
1782 val = atoi(array[1]);
1783 if (height > val) height = val;
1784 lives_strfreev(array);
1785 continue;
1786 }
1787
1788 if (!strncmp(checks[r], "maxw=", 5)) {
1789 array = lives_strsplit(checks[r], "=", 2);
1790 val = atoi(array[1]);
1791 if (width > val) width = val;
1792 lives_strfreev(array);
1793 continue;
1794 }
1795
1796 if (!strncmp(checks[r], "asigned=", 8) &&
1797 ((mainw->save_with_sound || rdet) && (!resaudw ||
1798 !resaudw->aud_checkbutton ||
1799 lives_toggle_button_get_active
1800 (LIVES_TOGGLE_BUTTON(resaudw->aud_checkbutton)))) &&
1801 prefs->encoder.audio_codec != AUDIO_CODEC_NONE
1802 && arate != 0 && achans != 0 && asampsize != 0) {
1803 array = lives_strsplit(checks[r], "=", 2);
1804 if (!strcmp(array[1], "signed")) {
1805 asigned = 1;
1806 }
1807
1808 if (!strcmp(array[1], "unsigned")) {
1809 asigned = 2;
1810 }
1811
1812 lives_strfreev(array);
1813
1814 if (asigned != 0 && !capable->has_sox_sox) {
1815 do_encoder_sox_error();
1816 lives_strfreev(checks);
1817 return FALSE;
1818 }
1819 continue;
1820 }
1821
1822 if (!strncmp(checks[r], "arate=", 6) && ((mainw->save_with_sound || rdet) && (!resaudw ||
1823 !resaudw->aud_checkbutton ||
1824 lives_toggle_button_get_active
1825 (LIVES_TOGGLE_BUTTON
1826 (resaudw->aud_checkbutton)))) &&
1827 prefs->encoder.audio_codec != AUDIO_CODEC_NONE && arate != 0 && achans != 0 && asampsize != 0) {
1828 // we only perform this test if we are encoding with audio
1829 // find next highest allowed rate from list,
1830 // if none are higher, use the highest
1831 int allowed_arate;
1832 best_arate_delta = 1000000000;
1833
1834 array = lives_strsplit(checks[r], "=", 2);
1835 numtok = get_token_count(array[1], ';');
1836 array2 = lives_strsplit(array[1], ";", numtok);
1837 for (i = 0; i < numtok; i++) {
1838 allowed_arate = atoi(array2[i]);
1839 if (allowed_arate >= arate) {
1840 if (allowed_arate - arate < best_arate_delta) {
1841 best_arate_delta = allowed_arate - arate;
1842 best_arate = allowed_arate;
1843 }
1844 } else if (allowed_arate > best_arate) best_arate = allowed_arate;
1845 }
1846 lives_strfreev(array2);
1847 lives_strfreev(array);
1848
1849 if (!capable->has_sox_sox) {
1850 do_encoder_sox_error();
1851 lives_strfreev(checks);
1852 return FALSE;
1853 }
1854 continue;
1855 }
1856
1857 if (!strncmp(checks[r], "hblock=", 7)) {
1858 // width must be a multiple of this
1859 array = lives_strsplit(checks[r], "=", 2);
1860 hblock = atoi(array[1]);
1861 width = (int)(width / hblock + .5) * hblock;
1862 lives_strfreev(array);
1863 continue;
1864 }
1865
1866 if (!strncmp(checks[r], "vblock=", 7)) {
1867 // height must be a multiple of this
1868 array = lives_strsplit(checks[r], "=", 2);
1869 vblock = atoi(array[1]);
1870 height = (int)(height / vblock + .5) * vblock;
1871 lives_strfreev(array);
1872 continue;
1873 }
1874
1875 if (!strncmp(checks[r], "aspect=", 7)) {
1876 // we calculate the nearest smaller frame size using aspect,
1877 // hblock and vblock
1878 calc_aspect = TRUE;
1879 array = lives_strsplit(checks[r], "=", 2);
1880 lives_snprintf(aspect_buffer, 512, "%s", array[1]);
1881 lives_strfreev(array);
1882 continue;
1883 }
1884 }
1885
1886 /// end restrictions
1887 lives_strfreev(checks);
1888
1889 if (!mainw->osc_auto && calc_aspect && !sizer) {
1890 // we calculate this last, after getting hblock and vblock sizes
1891 char **array3;
1892 double allowed_aspect;
1893 int xwidth = width;
1894 int xheight = height;
1895
1896 width = height = 1000000;
1897
1898 numtok = get_token_count(aspect_buffer, ';');
1899 array2 = lives_strsplit(aspect_buffer, ";", numtok);
1900
1901 // see if we can get a width:height which is nearer an aspect than
1902 // current width:height
1903
1904 for (i = 0; i < numtok; i++) {
1905 array3 = lives_strsplit(array2[i], ":", 2);
1906 allowed_aspect = lives_strtod(array3[0], NULL) / lives_strtod(array3[1], NULL);
1907 lives_strfreev(array3);
1908 minimise_aspect_delta(allowed_aspect, hblock, vblock, xwidth, xheight, &width, &height);
1909 }
1910 lives_strfreev(array2);
1911
1912 // allow override if current width and height are integer multiples of blocks
1913 if (owidth % hblock == 0 && oheight % vblock == 0) allow_aspect_override = TRUE;
1914
1915 // end recheck
1916 }
1917
1918 // fps can't be altered if we have a multitrack event_list
1919 if (mainw->multitrack && mainw->multitrack->event_list) best_fps_delta = 0.;
1920
1921 if (sizer) allow_aspect_override = FALSE;
1922 }
1923
1924 // if we have min or max size, make sure we fit within that
1925
1926 if (((width != owidth || height != oheight) && width * height > 0) || (best_fps_delta > 0.) || (best_arate_delta > 0 &&
1927 best_arate > 0) ||
1928 best_arate < 0 || asigned != 0 || swap_endian) {
1929 boolean ofx1_bool = mainw->fx1_bool;
1930 mainw->fx1_bool = FALSE;
1931 if ((width != owidth || height != oheight) && width * height > 0) {
1932 if (!capable->has_convert && !rdet && mainw->fx_candidates[FX_CANDIDATE_RESIZER].delegate == -1) {
1933 if (allow_aspect_override) {
1934 width = owidth;
1935 height = oheight;
1936 }
1937 }
1938 }
1939 if (rdet && !rdet->is_encoding) {
1940 rdet->arate = (int)atoi(lives_entry_get_text(LIVES_ENTRY(resaudw->entry_arate)));
1941 rdet->achans = (int)atoi(lives_entry_get_text(LIVES_ENTRY(resaudw->entry_achans)));
1942 rdet->asamps = (int)atoi(lives_entry_get_text(LIVES_ENTRY(resaudw->entry_asamps)));
1943 rdet->aendian = get_signed_endian(lives_toggle_button_get_active(LIVES_TOGGLE_BUTTON(resaudw->rb_unsigned)),
1944 lives_toggle_button_get_active(LIVES_TOGGLE_BUTTON(resaudw->rb_littleend)));
1945
1946 if (swap_endian || width != rdet->width || height != rdet->height || best_fps_delta != 0. || best_arate != rdet->arate ||
1947 ((asigned == 1 && (rdet->aendian & AFORM_UNSIGNED)) || (asigned == 2 && !(rdet->aendian & AFORM_SIGNED)))) {
1948
1949 if (rdet_suggest_values(width, height, best_fps, best_fps_num, best_fps_denom, best_arate, asigned, swap_endian,
1950 allow_aspect_override, (best_fps_delta == 0.))) {
1951 char *arate_string;
1952 rdet->width = width;
1953 rdet->height = height;
1954 if (best_arate != -1) rdet->arate = best_arate;
1955 else lives_toggle_button_set_active(LIVES_TOGGLE_BUTTON(resaudw->aud_checkbutton), FALSE);
1956
1957 if (asigned == 1) lives_toggle_button_set_active(LIVES_TOGGLE_BUTTON(resaudw->rb_signed), TRUE);
1958 else if (asigned == 2) lives_toggle_button_set_active(LIVES_TOGGLE_BUTTON(resaudw->rb_unsigned), TRUE);
1959
1960 if (swap_endian) {
1961 if (!lives_toggle_button_get_active(LIVES_TOGGLE_BUTTON(resaudw->rb_bigend)))
1962 lives_toggle_button_set_active(LIVES_TOGGLE_BUTTON(resaudw->rb_bigend), TRUE);
1963 else lives_toggle_button_set_active(LIVES_TOGGLE_BUTTON(resaudw->rb_littleend), TRUE);
1964 }
1965
1966 if (best_fps_delta > 0.) {
1967 if (best_fps_denom > 0) {
1968 rdet->fps = (best_fps_num * 1.) / (best_fps_denom * 1.);
1969 } else rdet->fps = best_fps;
1970 lives_spin_button_set_value(LIVES_SPIN_BUTTON(rdet->spinbutton_fps), rdet->fps);
1971 }
1972 lives_spin_button_set_value(LIVES_SPIN_BUTTON(rdet->spinbutton_width), rdet->width);
1973 lives_spin_button_set_value(LIVES_SPIN_BUTTON(rdet->spinbutton_height), rdet->height);
1974 if (best_arate != -1) {
1975 arate_string = lives_strdup_printf("%d", best_arate);
1976 lives_entry_set_text(LIVES_ENTRY(resaudw->entry_arate), arate_string);
1977 lives_free(arate_string);
1978 }
1979 rdet->suggestion_followed = TRUE;
1980 return TRUE;
1981 }
1982 }
1983 return FALSE;
1984 }
1985
1986 if (mainw->osc_auto || do_encoder_restrict_dialog(width, height, best_fps, best_fps_num, best_fps_denom, best_arate,
1987 asigned, swap_endian, allow_aspect_override, save_all)) {
1988 if (!mainw->fx1_bool && mainw->osc_enc_width == 0) {
1989 width = owidth;
1990 height = oheight;
1991 }
1992
1993 if (!auto_resample_resize(width, height, best_fps, best_fps_num, best_fps_denom, best_arate, asigned, swap_endian)) {
1994 mainw->fx1_bool = ofx1_bool;
1995 return FALSE;
1996 }
1997 } else {
1998 mainw->fx1_bool = ofx1_bool;
1999 return FALSE;
2000 }
2001 }
2002 return TRUE;
2003 }
2004
2005
filter_encoders_by_img_ext(LiVESList * encoders,const char * img_ext)2006 LiVESList *filter_encoders_by_img_ext(LiVESList * encoders, const char *img_ext) {
2007 LiVESList *encoder_capabilities = NULL;
2008 LiVESList *list = encoders, *listnext;
2009 int caps;
2010
2011 register int i;
2012
2013 char *blacklist[] = {
2014 NULL,
2015 NULL
2016 };
2017
2018 // something broke as of python 2.7.2, and python 3 files now just hang
2019 if (capable->python_version < 3000000) blacklist[0] = lives_strdup("multi_encoder3");
2020
2021 while (list) {
2022 boolean skip = FALSE;
2023 i = 0;
2024
2025 listnext = list->next;
2026
2027 while (blacklist[i]) {
2028 if (!strcmp((const char *)list->data, blacklist[i])) {
2029 // skip blacklisted encoders
2030 lives_free((livespointer)list->data);
2031 encoders = lives_list_delete_link(encoders, list);
2032 skip = TRUE;
2033 break;
2034 }
2035 i++;
2036 }
2037 if (skip) {
2038 list = listnext;
2039 continue;
2040 }
2041
2042 if (!strcmp(img_ext, LIVES_FILE_EXT_JPG)) {
2043 list = listnext;
2044 continue;
2045 }
2046
2047 if ((encoder_capabilities = plugin_request(PLUGIN_ENCODERS, (char *)list->data, "get_capabilities")) == NULL) {
2048 lives_free((livespointer)list->data);
2049 encoders = lives_list_delete_link(encoders, list);
2050 } else {
2051 caps = atoi((char *)lives_list_nth_data(encoder_capabilities, 0));
2052 if (!(caps & CAN_ENCODE_PNG) && !strcmp(img_ext, LIVES_FILE_EXT_PNG)) {
2053 lives_free((livespointer)list->data);
2054 encoders = lives_list_delete_link(encoders, list);
2055 }
2056
2057 lives_list_free_all(&encoder_capabilities);
2058 }
2059
2060 list = listnext;
2061 }
2062
2063 for (i = 0; blacklist[i]; i++) lives_free(blacklist[i]);
2064
2065 return encoders;
2066 }
2067
2068
2069 //////////////////////////////////////////////////////
2070 // decoder plugins
2071
decoder_plugin_move_to_first(const char * name)2072 boolean decoder_plugin_move_to_first(const char *name) {
2073 LiVESList *decoder_plugin, *last_decoder_plugin = NULL;
2074 decoder_plugin = mainw->decoder_list;
2075 while (decoder_plugin) {
2076 if (!strcmp((const char *)(decoder_plugin->data), name)) {
2077 if (last_decoder_plugin) {
2078 last_decoder_plugin->next = decoder_plugin->next;
2079 decoder_plugin->next = mainw->decoder_list;
2080 mainw->decoder_list = decoder_plugin;
2081 }
2082 return TRUE;
2083 }
2084 last_decoder_plugin = decoder_plugin;
2085 decoder_plugin = decoder_plugin->next;
2086 }
2087 return FALSE;
2088 }
2089
2090
2091
load_decoders(void)2092 LiVESList *load_decoders(void) {
2093 lives_decoder_sys_t *dplug;
2094 char *decplugdir = lives_strdup_printf("%s%s%s", prefs->lib_dir, PLUGIN_EXEC_DIR, PLUGIN_DECODERS);
2095 LiVESList *dlist = NULL;
2096 LiVESList *decoder_plugins_o = get_plugin_list(PLUGIN_DECODERS, TRUE, decplugdir, "-" DLL_NAME);
2097 LiVESList *decoder_plugins = decoder_plugins_o;
2098
2099 char *blacklist[3] = {
2100 "zyavformat_decoder",
2101 "ogg_theora_decoder",
2102 NULL
2103 };
2104
2105 const char *dplugname;
2106 boolean skip;
2107
2108 register int i;
2109
2110 while (decoder_plugins) {
2111 skip = FALSE;
2112 dplugname = (const char *)(decoder_plugins->data);
2113 for (i = 0; blacklist[i]; i++) {
2114 if (!strcmp(dplugname, blacklist[i])) {
2115 // skip blacklisted decoders
2116 skip = TRUE;
2117 break;
2118 }
2119 }
2120 if (!skip) {
2121 dplug = open_decoder_plugin((char *)decoder_plugins->data);
2122 if (dplug) dlist = lives_list_append(dlist, (livespointer)dplug);
2123 }
2124 lives_free((livespointer)decoder_plugins->data);
2125 decoder_plugins = decoder_plugins->next;
2126 }
2127
2128 lives_list_free(decoder_plugins_o);
2129
2130 if (!dlist) {
2131 char *msg = lives_strdup_printf(_("\n\nNo decoders found in %s !\n"), decplugdir);
2132 LIVES_WARN(msg);
2133 d_print(msg);
2134 lives_free(msg);
2135 }
2136
2137 lives_free(decplugdir);
2138 return dlist;
2139 }
2140
2141
set_cdata_memfuncs(lives_clip_data_t * cdata)2142 static void set_cdata_memfuncs(lives_clip_data_t *cdata) {
2143 // set specific memory functions for decoder plugins to use
2144 static malloc_f ext_malloc = (malloc_f) _ext_malloc;
2145 static free_f ext_free = (free_f) _ext_free;
2146 static memcpy_f ext_memcpy = (memcpy_f) _ext_memcpy;
2147 static memset_f ext_memset = (memset_f) _ext_memset;
2148 static memmove_f ext_memmove = (memmove_f) _ext_memmove;
2149 static realloc_f ext_realloc = (realloc_f) _ext_realloc;
2150 static calloc_f ext_calloc = (calloc_f) _ext_calloc;
2151 if (!cdata) return;
2152 cdata->ext_malloc = &ext_malloc;
2153 cdata->ext_free = &ext_free;
2154 cdata->ext_memcpy = &ext_memcpy;
2155 cdata->ext_memset = &ext_memset;
2156 cdata->ext_memmove = &ext_memmove;
2157 cdata->ext_realloc = &ext_realloc;
2158 cdata->ext_calloc = &ext_calloc;
2159 }
2160
2161
sanity_check_cdata(lives_clip_data_t * cdata)2162 static boolean sanity_check_cdata(lives_clip_data_t *cdata) {
2163 if (cdata->nframes <= 0 || cdata->nframes >= INT_MAX) {
2164 return FALSE;
2165 }
2166
2167 // no usable palettes found
2168 if (cdata->palettes[0] == WEED_PALETTE_END) return FALSE;
2169
2170 // all checks passed - OK
2171 return TRUE;
2172 }
2173
2174
2175 typedef struct {
2176 LiVESList *disabled;
2177 lives_decoder_t *dplug;
2178 lives_clip_t *sfile;
2179 } tdp_data;
2180
clone_decoder(int fileno)2181 lives_decoder_t *clone_decoder(int fileno) {
2182 lives_decoder_t *dplug;
2183 const lives_decoder_sys_t *dpsys;
2184 lives_clip_data_t *cdata;
2185
2186 if (!mainw->files[fileno] || !mainw->files[fileno]->ext_src) return NULL;
2187
2188 cdata = ((lives_decoder_sys_t *)((lives_decoder_t *)mainw->files[fileno]->ext_src)->decoder)->get_clip_data
2189 (NULL, ((lives_decoder_t *)mainw->files[fileno]->ext_src)->cdata);
2190
2191 if (!cdata) return NULL;
2192
2193 dplug = (lives_decoder_t *)lives_calloc(1, sizeof(lives_decoder_t));
2194 dpsys = ((lives_decoder_t *)mainw->files[fileno]->ext_src)->decoder;
2195
2196 dplug->decoder = dpsys;
2197 dplug->cdata = cdata;
2198 dplug->refs = 1;
2199 set_cdata_memfuncs((lives_clip_data_t *)cdata);
2200 cdata->rec_rowstrides = NULL;
2201 return dplug;
2202 }
2203
2204
try_decoder_plugins(char * file_name,LiVESList * disabled,const lives_clip_data_t * fake_cdata)2205 static lives_decoder_t *try_decoder_plugins(char *file_name, LiVESList * disabled, const lives_clip_data_t *fake_cdata) {
2206 // here we test each decoder in turn to see if it can open "file_name"
2207
2208 // if we are reopening a clip, then fake cdata is a partially initialised cdata, but with only the frame count and fps set
2209 // this allows the decoder plugins to startup quicker as they don't have to seek to the last frame or calculate the fps.
2210
2211 // we pass this to each decoder in turn and check what it returns. If the values look sane then we use that decoder,
2212 // otherwise we try the next one.
2213
2214 // when reloading clips we try the decoder which last opened them first, othwerwise they could get picked up by another
2215 // decoder and the frames could come out different
2216
2217 lives_decoder_t *dplug = (lives_decoder_t *)lives_calloc(1, sizeof(lives_decoder_t));
2218 LiVESList *decoder_plugin = mainw->decoder_list;
2219 //LiVESList *last_decoder_plugin = NULL;
2220
2221 dplug->refs = 1;
2222
2223 if (fake_cdata) {
2224 set_cdata_memfuncs((lives_clip_data_t *)fake_cdata);
2225 //if (prefs->vj_mode) {
2226 ((lives_clip_data_t *)fake_cdata)->seek_flag = LIVES_SEEK_FAST;
2227 //}
2228 }
2229
2230 while (decoder_plugin) {
2231 lives_decoder_sys_t *dpsys = (lives_decoder_sys_t *)decoder_plugin->data;
2232 if (lives_list_strcmp_index(disabled, dpsys->name, FALSE) != -1) {
2233 // check if (user) disabled this decoder
2234 decoder_plugin = decoder_plugin->next;
2235 continue;
2236 }
2237
2238 //#define DEBUG_DECPLUG
2239 #ifdef DEBUG_DECPLUG
2240 g_print("trying decoder %s\n", dpsys->name);
2241 #endif
2242
2243 dplug->cdata = (dpsys->get_clip_data)(file_name, fake_cdata);
2244
2245 if (dplug->cdata) {
2246 // check for sanity
2247 //g_print("Checking return data from %s\n", dpsys->name);
2248 if (lsd_check_match((lives_struct_def_t *)get_lsd(LIVES_STRUCT_CLIP_DATA_T),
2249 &dplug->cdata->lsd)) {
2250 g_printerr("Error in cdata received from decoder plugin:\n%s\nAborting.", dpsys->name);
2251 abort();
2252 }
2253 if (!sanity_check_cdata(dplug->cdata)) {
2254 //last_decoder_plugin = decoder_plugin;
2255 decoder_plugin = decoder_plugin->next;
2256 continue;
2257 }
2258 set_cdata_memfuncs(dplug->cdata);
2259 //////////////////////
2260
2261 dplug->decoder = dpsys;
2262
2263 /* if (strncmp(dpsys->name, "zz", 2)) { */
2264 /* // if libav decoder opened us, move it to the first, since it can save time */
2265 /* if (last_decoder_plugin) { */
2266 /* last_decoder_plugin->next = decoder_plugin->next; */
2267 /* decoder_plugin->next = mainw->decoder_list; */
2268 /* mainw->decoder_list = decoder_plugin; */
2269 /* } */
2270 break;
2271 }
2272 //last_decoder_plugin = decoder_plugin;
2273 decoder_plugin = decoder_plugin->next;
2274 }
2275
2276 if (!decoder_plugin) {
2277 lives_freep((void **)&dplug);
2278 } else {
2279 dplug->cdata->rec_rowstrides = NULL;
2280 }
2281
2282 return dplug;
2283 }
2284
2285
get_decoder_cdata(int fileno,LiVESList * disabled,const lives_clip_data_t * fake_cdata)2286 const lives_clip_data_t *get_decoder_cdata(int fileno, LiVESList * disabled, const lives_clip_data_t *fake_cdata) {
2287 // pass file to each decoder (demuxer) plugin in turn, until we find one that can parse
2288 // the file
2289 // NULL is returned if no decoder plugin recognises the file - then we
2290 // fall back to other methods
2291
2292 // otherwise we return data for the clip as supplied by the decoder plugin
2293
2294 // If the file does not exist, we set mainw->error=TRUE and return NULL
2295
2296 // If we find a plugin we also set sfile->ext_src to point to a newly created decoder_plugin_t
2297
2298 lives_decoder_t *dplug;
2299
2300 LiVESList *xdisabled;
2301
2302 lives_clip_t *sfile = mainw->files[fileno];
2303
2304 char decplugname[PATH_MAX];
2305
2306 mainw->error = FALSE;
2307
2308 if (!lives_file_test(sfile->file_name, LIVES_FILE_TEST_EXISTS)) {
2309 mainw->error = TRUE;
2310 return NULL;
2311 }
2312
2313 lives_memset(decplugname, 0, 1);
2314
2315 // check sfile->file_name against each decoder plugin,
2316 // until we get non-NULL cdata
2317
2318 sfile->ext_src = NULL;
2319 sfile->ext_src_type = LIVES_EXT_SRC_NONE;
2320
2321 lives_set_cursor_style(LIVES_CURSOR_BUSY, NULL);
2322
2323 xdisabled = lives_list_copy(disabled);
2324
2325 if (fake_cdata) {
2326 get_clip_value(fileno, CLIP_DETAILS_DECODER_NAME, decplugname, PATH_MAX);
2327 if (*decplugname) {
2328 LiVESList *decoder_plugin = mainw->decoder_list;
2329 xdisabled = lives_list_remove(xdisabled, decplugname);
2330 while (decoder_plugin) {
2331 lives_decoder_sys_t *dpsys = (lives_decoder_sys_t *)decoder_plugin->data;
2332 if (!strcmp(dpsys->name, decplugname)) {
2333 mainw->decoder_list = lives_list_move_to_first(mainw->decoder_list, decoder_plugin);
2334 break;
2335 }
2336 decoder_plugin = decoder_plugin->next;
2337 }
2338 }
2339 }
2340
2341 /// TODO: background thread so we can animate GUI
2342 dplug = try_decoder_plugins(fake_cdata == NULL ? sfile->file_name : NULL, xdisabled, fake_cdata);
2343
2344 if (xdisabled) lives_list_free(xdisabled);
2345
2346 lives_set_cursor_style(LIVES_CURSOR_NORMAL, NULL);
2347
2348 if (dplug) {
2349 d_print(_(" using %s"), dplug->decoder->version());
2350 sfile->ext_src = dplug;
2351 sfile->ext_src_type = LIVES_EXT_SRC_DECODER;
2352 return dplug->cdata;
2353 }
2354
2355 if (dplug) return dplug->cdata;
2356 return NULL;
2357 }
2358
2359
2360 // close one instance of dplug
close_decoder_plugin(lives_decoder_t * dplug)2361 void close_decoder_plugin(lives_decoder_t *dplug) {
2362 lives_clip_data_t *cdata;
2363
2364 if (!dplug) return;
2365
2366 dplug->refs--;
2367 if (dplug->refs) return;
2368
2369 cdata = dplug->cdata;
2370
2371 if (cdata) {
2372 if (cdata->rec_rowstrides) {
2373 lives_free(cdata->rec_rowstrides);
2374 cdata->rec_rowstrides = NULL;
2375 }
2376 (*dplug->decoder->clip_data_free)(cdata);
2377 }
2378 lives_free(dplug);
2379 }
2380
2381
close_clip_decoder(int clipno)2382 void close_clip_decoder(int clipno) {
2383 if (!IS_VALID_CLIP(clipno)) return;
2384 else {
2385 lives_clip_t *sfile = mainw->files[clipno];
2386 if (sfile->ext_src && sfile->ext_src_type == LIVES_EXT_SRC_DECODER) {
2387 char *cwd = lives_get_current_dir();
2388 char *ppath = lives_build_filename(prefs->workdir, sfile->handle, NULL);
2389 lives_chdir(ppath, FALSE);
2390 lives_free(ppath);
2391 close_decoder_plugin((lives_decoder_t *)sfile->ext_src);
2392 sfile->ext_src = NULL;
2393 sfile->ext_src_type = LIVES_EXT_SRC_NONE;
2394 lives_chdir(cwd, FALSE);
2395 lives_free(cwd);
2396 }
2397 }
2398 }
2399
2400
unload_decoder_plugin(lives_decoder_sys_t * dplug)2401 static void unload_decoder_plugin(lives_decoder_sys_t *dplug) {
2402 if (dplug->module_unload)(*dplug->module_unload)();
2403
2404 lives_freep((void **)&dplug->name);
2405
2406 dlclose(dplug->handle);
2407 lives_free(dplug);
2408 }
2409
2410
unload_decoder_plugins(void)2411 void unload_decoder_plugins(void) {
2412 LiVESList *dplugs = mainw->decoder_list;
2413
2414 while (dplugs) {
2415 unload_decoder_plugin((lives_decoder_sys_t *)dplugs->data);
2416 dplugs = dplugs->next;
2417 }
2418
2419 lives_list_free(mainw->decoder_list);
2420 mainw->decoder_list = NULL;
2421 mainw->decoders_loaded = FALSE;
2422 }
2423
2424
chill_decoder_plugin(int fileno)2425 boolean chill_decoder_plugin(int fileno) {
2426 lives_clip_t *sfile = mainw->files[fileno];
2427 if (IS_NORMAL_CLIP(fileno) && sfile->clip_type == CLIP_TYPE_FILE && sfile->ext_src) {
2428 lives_decoder_t *dplug = (lives_decoder_t *)sfile->ext_src;
2429 lives_decoder_sys_t *dpsys = (lives_decoder_sys_t *)dplug->decoder;
2430 lives_clip_data_t *cdata = dplug->cdata;
2431 if (cdata)
2432 if (dpsys->chill_out) return (*dpsys->chill_out)(cdata);
2433 }
2434 return FALSE;
2435 }
2436
2437
open_decoder_plugin(const char * plname)2438 lives_decoder_sys_t *open_decoder_plugin(const char *plname) {
2439 lives_decoder_sys_t *dplug;
2440 char *plugname, *tmp;
2441 boolean OK = TRUE;
2442 const char *err;
2443 int dlflags = RTLD_NOW | RTLD_LOCAL;
2444
2445 dplug = (lives_decoder_sys_t *)lives_calloc(1, sizeof(lives_decoder_sys_t));
2446
2447 dplug->name = NULL;
2448
2449 tmp = lives_strdup_printf("%s.%s", plname, DLL_NAME);
2450 plugname = lives_build_filename(prefs->lib_dir, PLUGIN_EXEC_DIR, PLUGIN_DECODERS, tmp, NULL);
2451 lives_free(tmp);
2452
2453 #ifdef RTLD_DEEPBIND
2454 dlflags |= RTLD_DEEPBIND;
2455 #endif
2456
2457 dplug->handle = dlopen(plugname, dlflags);
2458 lives_free(plugname);
2459
2460 if (!dplug->handle) {
2461 d_print(_("\n\nFailed to open decoder plugin %s\nError was %s\n"), plname, dlerror());
2462 lives_free(dplug);
2463 return NULL;
2464 }
2465
2466 if ((dplug->version = (const char *(*)())dlsym(dplug->handle, "version")) == NULL) {
2467 OK = FALSE;
2468 }
2469 if ((dplug->get_clip_data = (lives_clip_data_t *(*)(char *, const lives_clip_data_t *))
2470 dlsym(dplug->handle, "get_clip_data")) == NULL) {
2471 OK = FALSE;
2472 }
2473 if ((dplug->get_frame = (boolean(*)(const lives_clip_data_t *, int64_t, int *, int, void **))
2474 dlsym(dplug->handle, "get_frame")) == NULL) {
2475 OK = FALSE;
2476 }
2477 if ((dplug->clip_data_free = (void (*)(lives_clip_data_t *))dlsym(dplug->handle, "clip_data_free")) == NULL) {
2478 OK = FALSE;
2479 }
2480
2481 if (!OK) {
2482 d_print(_("\n\nDecoder plugin %s\nis missing a mandatory function.\nUnable to use it.\n"), plname);
2483 unload_decoder_plugin(dplug);
2484 lives_free(dplug);
2485 return NULL;
2486 }
2487
2488 // optional
2489 dplug->module_check_init = (const char *(*)())dlsym(dplug->handle, "module_check_init");
2490 dplug->chill_out = (boolean(*)(const lives_clip_data_t *))dlsym(dplug->handle, "chill_out");
2491 dplug->set_palette = (boolean(*)(lives_clip_data_t *))dlsym(dplug->handle, "set_palette");
2492 dplug->module_unload = (void (*)())dlsym(dplug->handle, "module_unload");
2493 dplug->rip_audio = (int64_t (*)(const lives_clip_data_t *, const char *, int64_t, int64_t, unsigned char **))
2494 dlsym(dplug->handle, "rip_audio");
2495 dplug->rip_audio_cleanup = (void (*)(const lives_clip_data_t *))dlsym(dplug->handle, "rip_audio_cleanup");
2496
2497 if (dplug->module_check_init) {
2498 err = (*dplug->module_check_init)();
2499
2500 if (err) {
2501 lives_snprintf(mainw->msg, MAINW_MSG_SIZE, "%s", err);
2502 unload_decoder_plugin(dplug);
2503 lives_free(dplug);
2504 return NULL;
2505 }
2506 }
2507
2508 dplug->name = lives_strdup(plname);
2509 return dplug;
2510 }
2511
2512
get_mime_type(char * text,int maxlen,const lives_clip_data_t * cdata)2513 void get_mime_type(char *text, int maxlen, const lives_clip_data_t *cdata) {
2514 char *audname;
2515
2516 if (!*cdata->container_name) lives_snprintf(text, maxlen, "%s", _("unknown"));
2517 else lives_snprintf(text, maxlen, "%s", cdata->container_name);
2518
2519 if (!*cdata->video_name && !*cdata->audio_name) return;
2520
2521 if (!*cdata->video_name) lives_strappend(text, maxlen, _("/unknown"));
2522 else {
2523 char *vidname = lives_strdup_printf("/%s", cdata->video_name);
2524 lives_strappend(text, maxlen, vidname);
2525 lives_free(vidname);
2526 }
2527
2528 if (!*cdata->audio_name) {
2529 if (cfile->achans == 0) return;
2530 audname = lives_strdup_printf("/%s", _("unknown"));
2531 } else
2532 audname = lives_strdup_printf("/%s", cdata->audio_name);
2533 lives_strappend(text, maxlen, audname);
2534 lives_free(audname);
2535 }
2536
2537
dpa_ok_clicked(LiVESButton * button,livespointer user_data)2538 static void dpa_ok_clicked(LiVESButton * button, livespointer user_data) {
2539 lives_general_button_clicked(button, NULL);
2540
2541 if (prefsw) {
2542 lives_window_present(LIVES_WINDOW(prefsw->prefs_dialog));
2543 lives_xwindow_raise(lives_widget_get_xwindow(prefsw->prefs_dialog));
2544 if (string_lists_differ(future_prefs->disabled_decoders, future_prefs->disabled_decoders_new))
2545 apply_button_set_enabled(NULL, NULL);
2546 }
2547
2548 lives_list_free_all(&future_prefs->disabled_decoders);
2549
2550 future_prefs->disabled_decoders = future_prefs->disabled_decoders_new;
2551 }
2552
2553
dpa_cancel_clicked(LiVESButton * button,livespointer user_data)2554 static void dpa_cancel_clicked(LiVESButton * button, livespointer user_data) {
2555 lives_general_button_clicked(button, NULL);
2556
2557 if (prefsw) {
2558 lives_window_present(LIVES_WINDOW(prefsw->prefs_dialog));
2559 lives_xwindow_raise(lives_widget_get_xwindow(prefsw->prefs_dialog));
2560 }
2561
2562 lives_list_free_all(&future_prefs->disabled_decoders_new);
2563 }
2564
2565
on_dpa_cb_toggled(LiVESToggleButton * button,const char * decname)2566 static void on_dpa_cb_toggled(LiVESToggleButton * button, const char *decname) {
2567 if (!lives_toggle_button_get_active(button))
2568 // unchecked is disabled
2569 future_prefs->disabled_decoders_new = lives_list_append(future_prefs->disabled_decoders_new, lives_strdup(decname));
2570 else
2571 future_prefs->disabled_decoders_new = lives_list_delete_string(future_prefs->disabled_decoders_new, decname);
2572 }
2573
2574
on_decplug_advanced_clicked(LiVESButton * button,livespointer user_data)2575 void on_decplug_advanced_clicked(LiVESButton * button, livespointer user_data) {
2576 LiVESList *decoder_plugin;
2577
2578 LiVESWidget *hbox;
2579 LiVESWidget *vbox;
2580 LiVESWidget *checkbutton;
2581 LiVESWidget *scrolledwindow;
2582 LiVESWidget *label;
2583 LiVESWidget *dialog;
2584 LiVESWidget *dialog_vbox;
2585 LiVESWidget *cancelbutton;
2586 LiVESWidget *okbutton;
2587
2588 char *ltext;
2589 char *decplugdir = lives_strdup_printf("%s%s%s", prefs->lib_dir, PLUGIN_EXEC_DIR, PLUGIN_DECODERS);
2590
2591 if (!mainw->decoders_loaded) {
2592 mainw->decoder_list = load_decoders();
2593 mainw->decoders_loaded = TRUE;
2594 }
2595
2596 decoder_plugin = mainw->decoder_list;
2597
2598 dialog = lives_standard_dialog_new(_("Decoder Plugins"), FALSE, DEF_DIALOG_WIDTH, DEF_DIALOG_HEIGHT);
2599
2600 dialog_vbox = lives_dialog_get_content_area(LIVES_DIALOG(dialog));
2601
2602 vbox = lives_vbox_new(FALSE, 0);
2603
2604 scrolledwindow = lives_standard_scrolled_window_new(RFX_WINSIZE_H, RFX_WINSIZE_V, vbox);
2605
2606 lives_container_add(LIVES_CONTAINER(dialog_vbox), scrolledwindow);
2607
2608 label = lives_standard_label_new(_("Enabled Video Decoders (uncheck to disable)"));
2609 lives_box_pack_start(LIVES_BOX(vbox), label, FALSE, FALSE, widget_opts.packing_height);
2610
2611 while (decoder_plugin) {
2612 lives_decoder_sys_t *dpsys = (lives_decoder_sys_t *)decoder_plugin->data;
2613 hbox = lives_hbox_new(FALSE, 0);
2614 lives_box_pack_start(LIVES_BOX(vbox), hbox, FALSE, FALSE, widget_opts.packing_height);
2615 ltext = lives_strdup_printf("%s (%s)", dpsys->name, (*dpsys->version)());
2616
2617 widget_opts.mnemonic_label = FALSE;
2618 checkbutton = lives_standard_check_button_new(ltext, lives_list_strcmp_index(future_prefs->disabled_decoders, dpsys->name,
2619 FALSE) == -1,
2620 LIVES_BOX(hbox), NULL);
2621 widget_opts.mnemonic_label = TRUE;
2622
2623 lives_free(ltext);
2624
2625 lives_signal_sync_connect_after(LIVES_GUI_OBJECT(checkbutton), LIVES_WIDGET_TOGGLED_SIGNAL,
2626 LIVES_GUI_CALLBACK(on_dpa_cb_toggled),
2627 (livespointer)dpsys->name);
2628
2629 decoder_plugin = decoder_plugin->next;
2630 }
2631
2632 cancelbutton = lives_dialog_add_button_from_stock(LIVES_DIALOG(dialog), LIVES_STOCK_CANCEL, NULL,
2633 LIVES_RESPONSE_CANCEL);
2634
2635 okbutton = lives_dialog_add_button_from_stock(LIVES_DIALOG(dialog), LIVES_STOCK_OK, NULL,
2636 LIVES_RESPONSE_OK);
2637
2638 lives_button_grab_default_special(okbutton);
2639
2640 lives_signal_connect(LIVES_GUI_OBJECT(cancelbutton), LIVES_WIDGET_CLICKED_SIGNAL,
2641 LIVES_GUI_CALLBACK(dpa_cancel_clicked),
2642 NULL);
2643
2644 lives_signal_connect(LIVES_GUI_OBJECT(okbutton), LIVES_WIDGET_CLICKED_SIGNAL,
2645 LIVES_GUI_CALLBACK(dpa_ok_clicked),
2646 NULL);
2647
2648 lives_widget_show_all(dialog);
2649 lives_window_present(LIVES_WINDOW(dialog));
2650 lives_xwindow_raise(lives_widget_get_xwindow(dialog));
2651
2652 future_prefs->disabled_decoders_new = lives_list_copy_strings(future_prefs->disabled_decoders);
2653
2654 lives_free(decplugdir);
2655 }
2656
2657 ///////////////////////////////////////////////////////
2658 // rfx plugin functions
2659
2660
check_rfx_for_lives(lives_rfx_t * rfx)2661 boolean check_rfx_for_lives(lives_rfx_t *rfx) {
2662 // check that an RFX is suitable for loading (cf. check_for_lives in effects-weed.c)
2663 if (rfx->num_in_channels == 2 && rfx->props & RFX_PROPS_MAY_RESIZE) {
2664 d_print(_("Failed to load %s, transitions may not resize.\n"), rfx->name);
2665 return FALSE;
2666 }
2667 return TRUE;
2668 }
2669
2670
do_rfx_cleanup(lives_rfx_t * rfx)2671 void do_rfx_cleanup(lives_rfx_t *rfx) {
2672 char *com;
2673 char *dir = NULL;
2674
2675 /// skip cleanup if menuentry is "apply current realtime effects"
2676 if (rfx == &mainw->rendered_fx[0]) return;
2677
2678 switch (rfx->status) {
2679 case RFX_STATUS_BUILTIN:
2680 dir = lives_build_filename(prefs->lib_dir, PLUGIN_EXEC_DIR, NULL);
2681 com = lives_strdup_printf("%s plugin_clear \"%s\" %d %d \"%s\" \"%s\" \"%s\"", prefs->backend_sync,
2682 cfile->handle, cfile->start, cfile->end, dir,
2683 PLUGIN_RENDERED_EFFECTS_BUILTIN, rfx->name);
2684 break;
2685 case RFX_STATUS_CUSTOM:
2686 com = lives_strdup_printf("%s plugin_clear \"%s\" %d %d \"%s\" \"%s\" \"%s\"", prefs->backend_sync,
2687 cfile->handle, cfile->start, cfile->end, prefs->config_datadir,
2688 PLUGIN_RENDERED_EFFECTS_CUSTOM, rfx->name);
2689 break;
2690 case RFX_STATUS_TEST:
2691 com = lives_strdup_printf("%s plugin_clear \"%s\" %d %d \"%s\" \"%s\" \"%s\"", prefs->backend_sync,
2692 cfile->handle, cfile->start, cfile->end, prefs->config_datadir,
2693 PLUGIN_RENDERED_EFFECTS_TEST, rfx->name);
2694 break;
2695 default:
2696 return;
2697 }
2698
2699 lives_freep((void **)&dir);
2700
2701 // if the command fails we just give a warning
2702 lives_system(com, FALSE);
2703 lives_free(com);
2704 }
2705
2706
render_fx_get_params(lives_rfx_t * rfx,const char * plugin_name,short status)2707 void render_fx_get_params(lives_rfx_t *rfx, const char *plugin_name, short status) {
2708 // create lives_param_t array from plugin supplied values
2709 LiVESList *parameter_list, *list;
2710 int param_idx, i;
2711 lives_param_t *cparam;
2712 char **param_array;
2713 char *line;
2714 int len;
2715
2716 switch (status) {
2717 case RFX_STATUS_BUILTIN:
2718 parameter_list = plugin_request_by_line(PLUGIN_RENDERED_EFFECTS_BUILTIN, plugin_name, "get_parameters");
2719 break;
2720 case RFX_STATUS_CUSTOM:
2721 parameter_list = plugin_request_by_line(PLUGIN_RENDERED_EFFECTS_CUSTOM, plugin_name, "get_parameters");
2722 break;
2723 case RFX_STATUS_SCRAP:
2724 parameter_list = plugin_request_by_line(PLUGIN_RFX_SCRAP, plugin_name, "get_parameters");
2725 break;
2726 case RFX_STATUS_INTERNAL:
2727 parameter_list = plugin_request_by_line(PLUGIN_RFX_SCRAP, plugin_name, "get_parameters");
2728 break;
2729 default:
2730 parameter_list = plugin_request_by_line(PLUGIN_RENDERED_EFFECTS_TEST, plugin_name, "get_parameters");
2731 break;
2732 }
2733
2734 if (!parameter_list) {
2735 rfx->num_params = 0;
2736 rfx->params = NULL;
2737 return;
2738 }
2739
2740 //threaded_dialog_spin(0.);
2741 rfx->num_params = lives_list_length(parameter_list);
2742 rfx->params = (lives_param_t *)lives_calloc(rfx->num_params, sizeof(lives_param_t));
2743 list = parameter_list;
2744
2745 for (param_idx = 0; param_idx < rfx->num_params; param_idx++) {
2746 line = (char *)list->data;
2747 list = list->next;
2748 len = get_token_count(line, (unsigned int)rfx->delim[0]);
2749
2750 if (len < 3) continue;
2751
2752 param_array = lives_strsplit(line, rfx->delim, -1);
2753
2754 cparam = &rfx->params[param_idx];
2755 cparam->name = lives_strdup(param_array[0]);
2756 cparam->label = lives_strdup(param_array[1]);
2757 cparam->desc = NULL;
2758 cparam->use_mnemonic = TRUE;
2759 cparam->hidden = 0;
2760 cparam->wrap = FALSE;
2761 cparam->transition = FALSE;
2762 cparam->step_size = 1.;
2763 cparam->group = 0;
2764 cparam->max = 0.;
2765 cparam->reinit = FALSE;
2766 cparam->changed = FALSE;
2767 cparam->edited = FALSE;
2768 cparam->change_blocked = FALSE;
2769 cparam->source = NULL;
2770 cparam->source_type = LIVES_RFX_SOURCE_RFX;
2771 cparam->special_type = LIVES_PARAM_SPECIAL_TYPE_NONE;
2772 cparam->special_type_index = 0;
2773 cparam->def = NULL;
2774 cparam->value = NULL;
2775
2776 #ifdef DEBUG_RENDER_FX_P
2777 lives_printerr("Got parameter %s\n", cparam->name);
2778 #endif
2779 cparam->dp = 0;
2780 cparam->list = NULL;
2781
2782 cparam->type = LIVES_PARAM_UNKNOWN;
2783
2784 if (!strncmp(param_array[2], "num", 3)) {
2785 cparam->dp = atoi(param_array[2] + 3);
2786 cparam->type = LIVES_PARAM_NUM;
2787 } else if (!strncmp(param_array[2], "bool", 4)) {
2788 cparam->type = LIVES_PARAM_BOOL;
2789 } else if (!strncmp(param_array[2], "colRGB24", 8)) {
2790 cparam->type = LIVES_PARAM_COLRGB24;
2791 } else if (!strncmp(param_array[2], "string", 8)) {
2792 cparam->type = LIVES_PARAM_STRING;
2793 } else if (!strncmp(param_array[2], "string_list", 8)) {
2794 cparam->type = LIVES_PARAM_STRING_LIST;
2795 } else continue;
2796
2797 if (cparam->dp) {
2798 double val;
2799 if (len < 6) continue;
2800 val = lives_strtod(param_array[3], NULL);
2801 cparam->value = lives_malloc(sizdbl);
2802 cparam->def = lives_malloc(sizdbl);
2803 set_double_param(cparam->def, val);
2804 set_double_param(cparam->value, val);
2805 cparam->min = lives_strtod(param_array[4], NULL);
2806 cparam->max = lives_strtod(param_array[5], NULL);
2807 if (len > 6) {
2808 cparam->step_size = lives_strtod(param_array[6], NULL);
2809 if (cparam->step_size == 0.) cparam->step_size = 1. / (double)lives_10pow(cparam->dp);
2810 else if (cparam->step_size < 0.) {
2811 cparam->step_size = -cparam->step_size;
2812 cparam->wrap = TRUE;
2813 }
2814 }
2815 } else if (cparam->type == LIVES_PARAM_COLRGB24) {
2816 short red;
2817 short green;
2818 short blue;
2819 if (len < 6) continue;
2820 red = (short)atoi(param_array[3]);
2821 green = (short)atoi(param_array[4]);
2822 blue = (short)atoi(param_array[5]);
2823 cparam->value = lives_malloc(sizeof(lives_colRGB48_t));
2824 cparam->def = lives_malloc(sizeof(lives_colRGB48_t));
2825 set_colRGB24_param(cparam->def, red, green, blue);
2826 set_colRGB24_param(cparam->value, red, green, blue);
2827 } else if (cparam->type == LIVES_PARAM_STRING) {
2828 if (len < 4) continue;
2829 cparam->value = (_(param_array[3]));
2830 cparam->def = (_(param_array[3]));
2831 if (len > 4) cparam->max = (double)atoi(param_array[4]);
2832 if (cparam->max == 0. || cparam->max > RFX_MAXSTRINGLEN) cparam->max = RFX_MAXSTRINGLEN;
2833 } else if (cparam->type == LIVES_PARAM_STRING_LIST) {
2834 if (len < 4) continue;
2835 cparam->value = lives_malloc(sizint);
2836 cparam->def = lives_malloc(sizint);
2837 *(int *)cparam->def = atoi(param_array[3]);
2838 if (len > 4) {
2839 cparam->list = array_to_string_list(param_array, 3, len);
2840 } else {
2841 set_int_param(cparam->def, 0);
2842 }
2843 set_int_param(cparam->value, get_int_param(cparam->def));
2844 } else {
2845 // int or bool
2846 int val;
2847 if (len < 4) continue;
2848 val = atoi(param_array[3]);
2849 cparam->value = lives_malloc(sizint);
2850 cparam->def = lives_malloc(sizint);
2851 set_int_param(cparam->def, val);
2852 set_int_param(cparam->value, val);
2853 if (cparam->type == LIVES_PARAM_BOOL) {
2854 cparam->min = 0;
2855 cparam->max = 1;
2856 if (len > 4) cparam->group = atoi(param_array[4]);
2857 } else {
2858 if (len < 6) continue;
2859 cparam->min = (double)atoi(param_array[4]);
2860 cparam->max = (double)atoi(param_array[5]);
2861 if (len > 6) {
2862 cparam->step_size = (double)atoi(param_array[6]);
2863 if (cparam->step_size == 0.) cparam->step_size = 1.;
2864 else if (cparam->step_size < 0.) {
2865 cparam->step_size = -cparam->step_size;
2866 cparam->wrap = TRUE;
2867 }
2868 }
2869 }
2870 }
2871
2872 for (i = 0; i < MAX_PARAM_WIDGETS; i++) {
2873 cparam->widgets[i] = NULL;
2874 }
2875 cparam->onchange = FALSE;
2876 lives_strfreev(param_array);
2877 }
2878 lives_list_free_all(¶meter_list);
2879 //threaded_dialog_spin(0.);
2880 }
2881
2882
array_to_string_list(char ** array,int offset,int len)2883 LiVESList *array_to_string_list(char **array, int offset, int len) {
2884 // build a LiVESList from an array.
2885 int i;
2886
2887 char *string, *tmp;
2888 LiVESList *slist = NULL;
2889
2890 for (i = offset + 1; i < len; i++) {
2891 string = subst((tmp = L2U8(array[i])), "\\n", "\n");
2892 lives_free(tmp);
2893
2894 // omit a last empty string
2895 if (i < len - 1 || *string) {
2896 slist = lives_list_append(slist, string);
2897 } else lives_free(string);
2898 }
2899
2900 return slist;
2901 }
2902
2903
cmp_menu_entries(livesconstpointer a,livesconstpointer b)2904 static int cmp_menu_entries(livesconstpointer a, livesconstpointer b) {
2905 return lives_utf8_strcmpfunc(((lives_rfx_t *)a)->menu_text, ((lives_rfx_t *)b)->menu_text, LIVES_INT_TO_POINTER(TRUE));
2906 }
2907
2908
sort_rfx_array(lives_rfx_t * in,int num)2909 void sort_rfx_array(lives_rfx_t *in, int num) {
2910 // sort rfx array into UTF-8 order by menu entry
2911 lives_rfx_t *rfx;
2912 int sorted = 1;
2913 register int i;
2914
2915 LiVESList *rfx_list = NULL, *xrfx_list;
2916 for (i = num; i > 0; i--) {
2917 rfx_list = lives_list_prepend(rfx_list, (livespointer)&in[i]);
2918 }
2919 rfx_list = lives_list_sort(rfx_list, cmp_menu_entries);
2920 rfx = mainw->rendered_fx = (lives_rfx_t *)lives_calloc((num + 1), sizeof(lives_rfx_t));
2921 rfx_copy(rfx, in, FALSE);
2922 xrfx_list = rfx_list;
2923 while (xrfx_list) {
2924 rfx_copy(&mainw->rendered_fx[sorted++], (lives_rfx_t *)(xrfx_list->data), FALSE);
2925 xrfx_list = xrfx_list->next;
2926 }
2927 lives_list_free(rfx_list);
2928 }
2929
2930
rfx_copy(lives_rfx_t * dest,lives_rfx_t * src,boolean full)2931 void rfx_copy(lives_rfx_t *dest, lives_rfx_t *src, boolean full) {
2932 // Warning, does not copy all fields (full will do that)
2933 lives_memcpy(dest->delim, src->delim, 2);
2934 dest->source = src->source;
2935 if (!full) {
2936 // ref. assigned memory
2937 src->source = NULL;
2938 dest->name = src->name;
2939 src->name = NULL;
2940 dest->menu_text = src->menu_text;
2941 src->menu_text = NULL;
2942 dest->action_desc = src->action_desc;
2943 src->action_desc = NULL;
2944 dest->params = src->params;
2945 src->params = NULL;
2946 } else {
2947 // deep copy
2948 if (dest->source_type == LIVES_RFX_SOURCE_WEED && dest->source) weed_instance_ref(dest->source);
2949 dest->name = lives_strdup(src->name);
2950 dest->menu_text = lives_strdup(src->menu_text);
2951 dest->action_desc = lives_strdup(src->action_desc);
2952 // TODO - copy params
2953 }
2954
2955 lives_snprintf(dest->rfx_version, 64, "%s", src->rfx_version);
2956 dest->min_frames = src->min_frames;
2957 dest->num_in_channels = src->num_in_channels;
2958 dest->status = src->status;
2959 dest->props = src->props;
2960 dest->source_type = src->source_type;
2961 dest->num_params = src->num_params;
2962 dest->is_template = src->is_template;
2963 dest->menuitem = src->menuitem;
2964 dest->gui_strings = lives_list_copy(src->gui_strings);
2965 dest->onchange_strings = lives_list_copy(src->onchange_strings);
2966 if (!full) return;
2967
2968 // TODO
2969
2970 }
2971
2972
rfx_params_free(lives_rfx_t * rfx)2973 void rfx_params_free(lives_rfx_t *rfx) {
2974 register int i;
2975 for (i = 0; i < rfx->num_params; i++) {
2976 if (rfx->params[i].type == LIVES_PARAM_UNDISPLAYABLE || rfx->params[i].type == LIVES_PARAM_UNKNOWN) continue;
2977 lives_free(rfx->params[i].name);
2978 lives_freep((void **)&rfx->params[i].def);
2979 lives_freep((void **)&rfx->params[i].value);
2980 lives_freep((void **)&rfx->params[i].label);
2981 lives_freep((void **)&rfx->params[i].desc);
2982 lives_list_free_all(&rfx->params[i].list);
2983 }
2984 }
2985
2986
rfx_free(lives_rfx_t * rfx)2987 void rfx_free(lives_rfx_t *rfx) {
2988 if (!rfx) return;
2989
2990 if (mainw->vrfx_update == rfx) mainw->vrfx_update = NULL;
2991
2992 lives_freep((void **)&rfx->name);
2993 lives_freep((void **)&rfx->menu_text);
2994 lives_freep((void **)&rfx->action_desc);
2995
2996 if (rfx->params) {
2997 rfx_params_free(rfx);
2998 lives_free(rfx->params);
2999 }
3000
3001 if (rfx->gui_strings) lives_list_free_all(&rfx->gui_strings);
3002 if (rfx->onchange_strings) lives_list_free_all(&rfx->onchange_strings);
3003
3004 if (rfx->source_type == LIVES_RFX_SOURCE_WEED && rfx->source) {
3005 weed_instance_unref((weed_plant_t *)rfx->source); // remove the ref we held
3006 }
3007 }
3008
3009
rfx_free_all(void)3010 void rfx_free_all(void) {
3011 register int i;
3012 for (i = 0; i <= mainw->num_rendered_effects_builtin + mainw->num_rendered_effects_custom
3013 + mainw->num_rendered_effects_test; i++) {
3014 rfx_free(&mainw->rendered_fx[i]);
3015 }
3016 lives_freep((void **)&mainw->rendered_fx);
3017 }
3018
3019
param_copy(lives_param_t * dest,lives_param_t * src,boolean full)3020 void param_copy(lives_param_t *dest, lives_param_t *src, boolean full) {
3021 // rfxbuilder.c uses this to copy params to a temporary copy and back again
3022
3023 dest->name = lives_strdup(src->name);
3024 dest->label = lives_strdup(src->label);
3025 dest->group = src->group;
3026 dest->onchange = src->onchange;
3027 dest->type = src->type;
3028 dest->dp = src->dp;
3029 dest->min = src->min;
3030 dest->max = src->max;
3031 dest->step_size = src->step_size;
3032 dest->wrap = src->wrap;
3033 dest->source = src->source;
3034 dest->reinit = src->reinit;
3035 dest->source_type = src->source_type;
3036 dest->value = dest->def = NULL;
3037 dest->list = NULL;
3038
3039 switch (dest->type) {
3040 case LIVES_PARAM_BOOL:
3041 dest->dp = 0;
3042 case LIVES_PARAM_NUM:
3043 if (!dest->dp) {
3044 dest->def = lives_malloc(sizint);
3045 lives_memcpy(dest->def, src->def, sizint);
3046 } else {
3047 dest->def = lives_malloc(sizdbl);
3048 lives_memcpy(dest->def, src->def, sizdbl);
3049 }
3050 break;
3051 case LIVES_PARAM_COLRGB24:
3052 dest->def = lives_malloc(sizeof(lives_colRGB48_t));
3053 lives_memcpy(dest->def, src->def, sizeof(lives_colRGB48_t));
3054 break;
3055 case LIVES_PARAM_STRING:
3056 dest->def = lives_strdup((char *)src->def);
3057 break;
3058 case LIVES_PARAM_STRING_LIST:
3059 dest->def = lives_malloc(sizint);
3060 set_int_param(dest->def, get_int_param(src->def));
3061 if (src->list) dest->list = lives_list_copy(src->list);
3062 break;
3063 default:
3064 break;
3065 }
3066 if (!full) return;
3067 // TODO - copy value, copy widgets
3068
3069 }
3070
3071
get_bool_param(void * value)3072 boolean get_bool_param(void *value) {
3073 boolean ret;
3074 lives_memcpy(&ret, value, 4);
3075 return ret;
3076 }
3077
3078
get_int_param(void * value)3079 int get_int_param(void *value) {
3080 int ret;
3081 lives_memcpy(&ret, value, sizint);
3082 return ret;
3083 }
3084
3085
get_double_param(void * value)3086 double get_double_param(void *value) {
3087 double ret;
3088 lives_memcpy(&ret, value, sizdbl);
3089 return ret;
3090 }
3091
3092
get_colRGB24_param(void * value,lives_colRGB48_t * rgb)3093 void get_colRGB24_param(void *value, lives_colRGB48_t *rgb) {
3094 lives_memcpy(rgb, value, sizeof(lives_colRGB48_t));
3095 }
3096
3097
get_colRGBA32_param(void * value,lives_colRGBA64_t * rgba)3098 void get_colRGBA32_param(void *value, lives_colRGBA64_t *rgba) {
3099 lives_memcpy(rgba, value, sizeof(lives_colRGBA64_t));
3100 }
3101
3102
set_bool_param(void * value,boolean _const)3103 void set_bool_param(void *value, boolean _const) {
3104 set_int_param(value, !!_const);
3105 }
3106
3107
set_string_param(void ** value_ptr,const char * _const,size_t maxlen)3108 void set_string_param(void **value_ptr, const char *_const, size_t maxlen) {
3109 lives_freep(value_ptr);
3110 *value_ptr = lives_strndup(_const, maxlen);
3111 }
3112
3113
set_int_param(void * value,int _const)3114 void set_int_param(void *value, int _const) {
3115 lives_memcpy(value, &_const, sizint);
3116 }
3117
3118
set_double_param(void * value,double _const)3119 void set_double_param(void *value, double _const) {
3120 lives_memcpy(value, &_const, sizdbl);
3121 }
3122
3123
set_rfx_param_by_name_string(lives_rfx_t * rfx,const char * name,const char * value,boolean update_visual)3124 boolean set_rfx_param_by_name_string(lives_rfx_t *rfx, const char *name, const char *value, boolean update_visual) {
3125 size_t len = strlen(value);
3126 lives_param_t *param = find_rfx_param_by_name(rfx, name);
3127 if (!param) return FALSE;
3128 set_string_param((void **)¶m->value, value, len > RFX_MAXSTRINGLEN ? RFX_MAXSTRINGLEN : len);
3129 if (update_visual) {
3130 LiVESList *list = NULL;
3131 char *tmp, *tmp2;
3132 list = lives_list_append(list, lives_strdup_printf("\"%s\"", (tmp = U82L(tmp2 = subst(value, "\"", "\\\"")))));
3133 lives_free(tmp); lives_free(tmp2);
3134 set_param_from_list(list, param, 0, FALSE, TRUE);
3135 lives_list_free_all(&list);
3136 }
3137 return TRUE;
3138 }
3139
3140
get_rfx_param_by_name_string(lives_rfx_t * rfx,const char * name,char ** return_value)3141 boolean get_rfx_param_by_name_string(lives_rfx_t *rfx, const char *name, char **return_value) {
3142 lives_param_t *param = find_rfx_param_by_name(rfx, name);
3143 if (!param) return FALSE;
3144 *return_value = lives_strndup(param->value, RFX_MAXSTRINGLEN);
3145 return TRUE;
3146 }
3147
3148
set_colRGB24_param(void * value,short red,short green,short blue)3149 void set_colRGB24_param(void *value, short red, short green, short blue) {
3150 lives_colRGB48_t *rgbp = (lives_colRGB48_t *)value;
3151
3152 if (red < 0) red = 0;
3153 if (red > 255) red = 255;
3154 if (green < 0) green = 0;
3155 if (green > 255) green = 255;
3156 if (blue < 0) blue = 0;
3157 if (blue > 255) blue = 255;
3158
3159 rgbp->red = red;
3160 rgbp->green = green;
3161 rgbp->blue = blue;
3162
3163 }
3164
3165
set_colRGBA32_param(void * value,short red,short green,short blue,short alpha)3166 void set_colRGBA32_param(void *value, short red, short green, short blue, short alpha) {
3167 lives_colRGBA64_t *rgbap = (lives_colRGBA64_t *)value;
3168 rgbap->red = red;
3169 rgbap->green = green;
3170 rgbap->blue = blue;
3171 rgbap->alpha = alpha;
3172 }
3173
3174
3175 ///////////////////////////////////////////////////////////////
3176
find_rfx_plugin_by_name(const char * name,short status)3177 int find_rfx_plugin_by_name(const char *name, short status) {
3178 int i;
3179 for (i = 1; i < mainw->num_rendered_effects_builtin + mainw->num_rendered_effects_custom +
3180 mainw->num_rendered_effects_test; i++) {
3181 if (mainw->rendered_fx[i].name && !strcmp(mainw->rendered_fx[i].name, name)
3182 && mainw->rendered_fx[i].status == status)
3183 return (int)i;
3184 }
3185 return -1;
3186 }
3187
3188
find_rfx_param_by_name(lives_rfx_t * rfx,const char * name)3189 lives_param_t *find_rfx_param_by_name(lives_rfx_t *rfx, const char *name) {
3190 int i;
3191 if (!rfx) return NULL;
3192 for (i = 0; i < rfx->num_params; i++) {
3193 if (!strcmp(name, rfx->params[i].name)) {
3194 return &rfx->params[i];
3195 }
3196 }
3197 return NULL;
3198 }
3199
3200
weed_params_to_rfx(int npar,weed_plant_t * inst,boolean show_reinits)3201 lives_param_t *weed_params_to_rfx(int npar, weed_plant_t *inst, boolean show_reinits) {
3202 int i, j;
3203 lives_param_t *rpar = (lives_param_t *)lives_calloc(npar, sizeof(lives_param_t));
3204 int param_type;
3205 char **list;
3206 LiVESList *gtk_list = NULL;
3207 char *string;
3208 int vali;
3209 double vald;
3210 weed_plant_t *gui = NULL;
3211 int listlen;
3212 int cspace, *cols = NULL, red_min = 0, red_max = 255, green_min = 0, green_max = 255, blue_min = 0, blue_max = 255,
3213 *maxi = NULL, *mini = NULL;
3214 double *colsd;
3215 double red_mind = 0., red_maxd = 1., green_mind = 0., green_maxd = 1., blue_mind = 0., blue_maxd = 1.,
3216 *maxd = NULL, *mind = NULL;
3217 int flags = 0;
3218 int nwpars = 0, poffset = 0;
3219 boolean col_int;
3220
3221 weed_plant_t *wtmpl;
3222 weed_plant_t **wpars = NULL, *wpar = NULL;
3223
3224 weed_plant_t *chann, *ctmpl;
3225 weed_plant_t *filter = weed_instance_get_filter(inst, TRUE);
3226
3227 wpars = weed_instance_get_in_params(inst, &nwpars);
3228
3229 for (i = 0; i < npar; i++) {
3230 if (i - poffset >= nwpars) {
3231 // handling for compound fx
3232 poffset += nwpars;
3233 if (wpars) lives_free(wpars);
3234 inst = weed_get_plantptr_value(inst, WEED_LEAF_HOST_NEXT_INSTANCE, NULL);
3235 wpars = weed_instance_get_in_params(inst, &nwpars);
3236 i--;
3237 continue;
3238 }
3239
3240 if (!wpars) {
3241 lives_free(rpar);
3242 return NULL;
3243 }
3244
3245 wpar = wpars[i - poffset];
3246 wtmpl = weed_param_get_template(wpar);
3247
3248 flags = weed_paramtmpl_get_flags(wtmpl);
3249
3250 rpar[i].flags = flags;
3251
3252 gui = weed_paramtmpl_get_gui(wtmpl, FALSE);
3253
3254 rpar[i].group = 0;
3255
3256 rpar[i].use_mnemonic = FALSE;
3257 rpar[i].hidden = 0;
3258 rpar[i].step_size = 1.;
3259 if (enabled_in_channels(filter, FALSE) == 2 && get_transition_param(filter, FALSE) == i) rpar[i].transition = TRUE;
3260 else rpar[i].transition = FALSE;
3261 rpar[i].wrap = FALSE;
3262 rpar[i].reinit = FALSE;
3263 rpar[i].change_blocked = FALSE;
3264 rpar[i].source = wtmpl;
3265 rpar[i].source_type = LIVES_RFX_SOURCE_WEED;
3266 rpar[i].special_type = LIVES_PARAM_SPECIAL_TYPE_NONE;
3267 rpar[i].special_type_index = 0;
3268 rpar[i].value = NULL;
3269 rpar[i].def = NULL;
3270
3271 if (flags & WEED_PARAMETER_VARIABLE_SIZE && !(flags & WEED_PARAMETER_VALUE_PER_CHANNEL)) {
3272 rpar[i].hidden |= HIDDEN_MULTI;
3273 rpar[i].multi = PVAL_MULTI_ANY;
3274 } else if (flags & WEED_PARAMETER_VALUE_PER_CHANNEL) {
3275 rpar[i].hidden |= HIDDEN_MULTI;
3276 rpar[i].multi = PVAL_MULTI_PER_CHANNEL;
3277 } else rpar[i].multi = PVAL_MULTI_NONE;
3278
3279 chann = get_enabled_channel(inst, 0, TRUE);
3280 ctmpl = weed_get_plantptr_value(chann, WEED_LEAF_TEMPLATE, NULL);
3281
3282 if (weed_get_boolean_value(ctmpl, WEED_LEAF_IS_AUDIO, NULL) == WEED_TRUE) {
3283 // dont hide multivalued params for audio effects
3284 rpar[i].hidden = 0;
3285 }
3286
3287 rpar[i].dp = 0;
3288 rpar[i].min = 0.;
3289 rpar[i].max = 0.;
3290 rpar[i].list = NULL;
3291
3292 rpar[i].reinit = 0;
3293 if (flags & WEED_PARAMETER_REINIT_ON_VALUE_CHANGE)
3294 rpar[i].reinit = REINIT_FUNCTIONAL;
3295 if (gui && (weed_gui_get_flags(gui) & WEED_GUI_REINIT_ON_VALUE_CHANGE))
3296 rpar[i].reinit |= REINIT_VISUAL;
3297
3298 if (!show_reinits && rpar[i].reinit != 0) rpar[i].hidden |= HIDDEN_NEEDS_REINIT;
3299
3300 ///////////////////////////////
3301 param_type = weed_paramtmpl_get_type(wtmpl);
3302
3303 switch (param_type) {
3304 case WEED_PARAM_SWITCH:
3305 if (weed_leaf_num_elements(wtmpl, WEED_LEAF_DEFAULT) > 1) {
3306 rpar[i].hidden |= HIDDEN_MULTI;
3307 }
3308 rpar[i].type = LIVES_PARAM_BOOL;
3309 rpar[i].value = lives_malloc(sizint);
3310 rpar[i].def = lives_malloc(sizint);
3311 if (!weed_paramtmpl_value_irrelevant(wtmpl) && weed_plant_has_leaf(wtmpl, WEED_LEAF_HOST_DEFAULT))
3312 vali = weed_get_boolean_value(wtmpl, WEED_LEAF_HOST_DEFAULT, NULL);
3313 else if (weed_leaf_num_elements(wtmpl, WEED_LEAF_DEFAULT) > 0)
3314 vali = weed_get_boolean_value(wtmpl, WEED_LEAF_DEFAULT, NULL);
3315 else vali = weed_get_boolean_value(wtmpl, WEED_LEAF_NEW_DEFAULT, NULL);
3316 set_int_param(rpar[i].def, vali);
3317 vali = weed_get_boolean_value(wpar, WEED_LEAF_VALUE, NULL);
3318 set_int_param(rpar[i].value, vali);
3319 if (weed_plant_has_leaf(wtmpl, WEED_LEAF_GROUP)) rpar[i].group = weed_get_int_value(wtmpl, WEED_LEAF_GROUP, NULL);
3320 break;
3321 case WEED_PARAM_INTEGER:
3322 if (weed_plant_has_leaf(wtmpl, WEED_LEAF_DEFAULT) && weed_leaf_num_elements(wtmpl, WEED_LEAF_DEFAULT) > 1) {
3323 rpar[i].hidden |= HIDDEN_MULTI;
3324 }
3325 rpar[i].type = LIVES_PARAM_NUM;
3326 rpar[i].value = lives_malloc(sizint);
3327 rpar[i].def = lives_malloc(sizint);
3328 if (!weed_paramtmpl_value_irrelevant(wtmpl) && weed_plant_has_leaf(wtmpl, WEED_LEAF_HOST_DEFAULT)) {
3329 vali = weed_get_int_value(wtmpl, WEED_LEAF_HOST_DEFAULT, NULL);
3330 } else if (weed_leaf_num_elements(wtmpl, WEED_LEAF_DEFAULT) > 0)
3331 vali = weed_get_int_value(wtmpl, WEED_LEAF_DEFAULT, NULL);
3332 else vali = weed_get_int_value(wtmpl, WEED_LEAF_NEW_DEFAULT, NULL);
3333 set_int_param(rpar[i].def, vali);
3334 vali = weed_get_int_value(wpar, WEED_LEAF_VALUE, NULL);
3335 set_int_param(rpar[i].value, vali);
3336 rpar[i].min = (double)weed_get_int_value(wtmpl, WEED_LEAF_MIN, NULL);
3337 rpar[i].max = (double)weed_get_int_value(wtmpl, WEED_LEAF_MAX, NULL);
3338 if (weed_paramtmpl_does_wrap(wtmpl)) rpar[i].wrap = TRUE;
3339 if (gui) {
3340 if (weed_plant_has_leaf(gui, WEED_LEAF_CHOICES)) {
3341 listlen = weed_leaf_num_elements(gui, WEED_LEAF_CHOICES);
3342 list = weed_get_string_array(gui, WEED_LEAF_CHOICES, NULL);
3343 for (j = 0; j < listlen; j++) {
3344 gtk_list = lives_list_append(gtk_list, list[j]);
3345 }
3346 lives_free(list);
3347 rpar[i].list = lives_list_copy(gtk_list);
3348 lives_list_free(gtk_list);
3349 gtk_list = NULL;
3350 rpar[i].type = LIVES_PARAM_STRING_LIST;
3351 rpar[i].max = listlen;
3352 } else if (weed_plant_has_leaf(gui, WEED_LEAF_STEP_SIZE))
3353 rpar[i].step_size = (double)weed_get_int_value(gui, WEED_LEAF_STEP_SIZE, NULL);
3354 if (rpar[i].step_size == 0.) rpar[i].step_size = 1.;
3355 }
3356 break;
3357 case WEED_PARAM_FLOAT:
3358 if (weed_plant_has_leaf(wtmpl, WEED_LEAF_DEFAULT) && weed_leaf_num_elements(wtmpl, WEED_LEAF_DEFAULT) > 1) {
3359 rpar[i].hidden |= HIDDEN_MULTI;
3360 }
3361 rpar[i].type = LIVES_PARAM_NUM;
3362 rpar[i].value = lives_malloc(sizdbl);
3363 rpar[i].def = lives_malloc(sizdbl);
3364 if (!weed_paramtmpl_value_irrelevant(wtmpl) && weed_plant_has_leaf(wtmpl, WEED_LEAF_HOST_DEFAULT))
3365 vald = weed_get_double_value(wtmpl, WEED_LEAF_HOST_DEFAULT, NULL);
3366 else if (weed_leaf_num_elements(wtmpl, WEED_LEAF_DEFAULT) > 0)
3367 vald = weed_get_double_value(wtmpl, WEED_LEAF_DEFAULT, NULL);
3368 else vald = weed_get_double_value(wtmpl, WEED_LEAF_NEW_DEFAULT, NULL);
3369 set_double_param(rpar[i].def, vald);
3370 vald = weed_get_double_value(wpar, WEED_LEAF_VALUE, NULL);
3371 set_double_param(rpar[i].value, vald);
3372 rpar[i].min = weed_get_double_value(wtmpl, WEED_LEAF_MIN, NULL);
3373 rpar[i].max = weed_get_double_value(wtmpl, WEED_LEAF_MAX, NULL);
3374 if (weed_paramtmpl_does_wrap(wtmpl)) rpar[i].wrap = TRUE;
3375 rpar[i].step_size = 0.;
3376 rpar[i].dp = 2;
3377 if (gui) {
3378 if (weed_plant_has_leaf(gui, WEED_LEAF_STEP_SIZE))
3379 rpar[i].step_size = weed_get_double_value(gui, WEED_LEAF_STEP_SIZE, NULL);
3380 if (weed_plant_has_leaf(gui, WEED_LEAF_DECIMALS))
3381 rpar[i].dp = weed_get_int_value(gui, WEED_LEAF_DECIMALS, NULL);
3382 }
3383 if (rpar[i].step_size == 0.) {
3384 if (rpar[i].max - rpar[i].min > 10. && !(rpar[i].min >= -10. && rpar[i].max <= 10.))
3385 rpar[i].step_size = 1.;
3386 else if (rpar[i].max - rpar[i].min > 1. && !(rpar[i].min >= -1. && rpar[i].max <= 1.))
3387 rpar[i].step_size = .1;
3388 else rpar[i].step_size = 1. / (double)lives_10pow(rpar[i].dp);
3389 }
3390 break;
3391 case WEED_PARAM_TEXT:
3392 if (weed_plant_has_leaf(wtmpl, WEED_LEAF_DEFAULT) && weed_leaf_num_elements(wtmpl, WEED_LEAF_DEFAULT) > 1) {
3393 rpar[i].hidden |= HIDDEN_MULTI;
3394 }
3395 rpar[i].type = LIVES_PARAM_STRING;
3396 if (!weed_paramtmpl_value_irrelevant(wtmpl) && weed_plant_has_leaf(wtmpl, WEED_LEAF_HOST_DEFAULT))
3397 string = weed_get_string_value(wtmpl, WEED_LEAF_HOST_DEFAULT, NULL);
3398 else if (weed_leaf_num_elements(wtmpl, WEED_LEAF_DEFAULT) > 0)
3399 string = weed_get_string_value(wtmpl, WEED_LEAF_DEFAULT, NULL);
3400 else string = weed_get_string_value(wtmpl, WEED_LEAF_NEW_DEFAULT, NULL);
3401 rpar[i].def = string;
3402 string = weed_get_string_value(wpar, WEED_LEAF_VALUE, NULL);
3403 rpar[i].value = string;
3404 rpar[i].max = 0.;
3405 if (gui && weed_plant_has_leaf(gui, WEED_LEAF_MAXCHARS)) {
3406 rpar[i].max = (double)weed_get_int_value(gui, WEED_LEAF_MAXCHARS, NULL);
3407 if (rpar[i].max < 0.) rpar[i].max = 0.;
3408 }
3409 break;
3410 case WEED_PARAM_COLOR:
3411 cspace = weed_get_int_value(wtmpl, WEED_LEAF_COLORSPACE, NULL);
3412 switch (cspace) {
3413 case WEED_COLORSPACE_RGB:
3414 if (weed_leaf_num_elements(wtmpl, WEED_LEAF_DEFAULT) > 3) {
3415 rpar[i].hidden |= HIDDEN_MULTI;
3416 }
3417 rpar[i].type = LIVES_PARAM_COLRGB24;
3418 rpar[i].value = lives_malloc(3 * sizint);
3419 rpar[i].def = lives_malloc(3 * sizint);
3420
3421 if (weed_leaf_seed_type(wtmpl, WEED_LEAF_DEFAULT) == WEED_SEED_INT) {
3422 if (!weed_paramtmpl_value_irrelevant(wtmpl) && weed_plant_has_leaf(wtmpl, WEED_LEAF_HOST_DEFAULT)) {
3423 cols = weed_get_int_array(wtmpl, WEED_LEAF_HOST_DEFAULT, NULL);
3424 } else if (weed_leaf_num_elements(wtmpl, WEED_LEAF_DEFAULT) > 0)
3425 cols = weed_get_int_array(wtmpl, WEED_LEAF_DEFAULT, NULL);
3426 else cols = weed_get_int_array(wtmpl, WEED_LEAF_NEW_DEFAULT, NULL);
3427 if (weed_leaf_num_elements(wtmpl, WEED_LEAF_MAX) == 1) {
3428 red_max = green_max = blue_max = weed_get_int_value(wtmpl, WEED_LEAF_MAX, NULL);
3429 } else {
3430 maxi = weed_get_int_array(wtmpl, WEED_LEAF_MAX, NULL);
3431 red_max = maxi[0];
3432 green_max = maxi[1];
3433 blue_max = maxi[2];
3434 }
3435 if (weed_leaf_num_elements(wtmpl, WEED_LEAF_MIN) == 1) {
3436 red_min = green_min = blue_min = weed_get_int_value(wtmpl, WEED_LEAF_MIN, NULL);
3437 } else {
3438 mini = weed_get_int_array(wtmpl, WEED_LEAF_MIN, NULL);
3439 red_min = mini[0];
3440 green_min = mini[1];
3441 blue_min = mini[2];
3442 }
3443 if (cols[0] < red_min) cols[0] = red_min;
3444 if (cols[1] < green_min) cols[1] = green_min;
3445 if (cols[2] < blue_min) cols[2] = blue_min;
3446 if (cols[0] > red_max) cols[0] = red_max;
3447 if (cols[1] > green_max) cols[1] = green_max;
3448 if (cols[2] > blue_max) cols[2] = blue_max;
3449 cols[0] = (double)(cols[0] - red_min) / (double)(red_max - red_min) * 255. + .49999;
3450 cols[1] = (double)(cols[1] - green_min) / (double)(green_max - green_min) * 255. + .49999;
3451 cols[2] = (double)(cols[2] - blue_min) / (double)(blue_max - blue_min) * 255. + .49999;
3452 col_int = TRUE;
3453 } else {
3454 if (!weed_paramtmpl_value_irrelevant(wtmpl) && weed_plant_has_leaf(wtmpl, WEED_LEAF_HOST_DEFAULT))
3455 colsd = weed_get_double_array(wtmpl, WEED_LEAF_HOST_DEFAULT, NULL);
3456 else if (weed_leaf_num_elements(wtmpl, WEED_LEAF_DEFAULT) > 0)
3457 colsd = weed_get_double_array(wtmpl, WEED_LEAF_DEFAULT, NULL);
3458 else colsd = weed_get_double_array(wtmpl, WEED_LEAF_DEFAULT, NULL);
3459 if (weed_leaf_num_elements(wtmpl, WEED_LEAF_MAX) == 1) {
3460 red_maxd = green_maxd = blue_maxd = weed_get_double_value(wtmpl, WEED_LEAF_MAX, NULL);
3461 } else {
3462 maxd = weed_get_double_array(wtmpl, WEED_LEAF_MAX, NULL);
3463 red_maxd = maxd[0];
3464 green_maxd = maxd[1];
3465 blue_maxd = maxd[2];
3466 }
3467 if (weed_leaf_num_elements(wtmpl, WEED_LEAF_MIN) == 1) {
3468 red_mind = green_mind = blue_mind = weed_get_double_value(wtmpl, WEED_LEAF_MIN, NULL);
3469 } else {
3470 mind = weed_get_double_array(wtmpl, WEED_LEAF_MIN, NULL);
3471 red_mind = mind[0];
3472 green_mind = mind[1];
3473 blue_mind = mind[2];
3474 }
3475 if (colsd[0] < red_mind) colsd[0] = red_mind;
3476 if (colsd[1] < green_mind) colsd[1] = green_mind;
3477 if (colsd[2] < blue_mind) colsd[2] = blue_mind;
3478 if (colsd[0] > red_maxd) colsd[0] = red_maxd;
3479 if (colsd[1] > green_maxd) colsd[1] = green_maxd;
3480 if (colsd[2] > blue_maxd) colsd[2] = blue_maxd;
3481 cols = (int *)lives_malloc(3 * sizint);
3482 cols[0] = (colsd[0] - red_mind) / (red_maxd - red_mind) * 255. + .49999;
3483 cols[1] = (colsd[1] - green_mind) / (green_maxd - green_mind) * 255. + .49999;
3484 cols[2] = (colsd[2] - blue_mind) / (blue_maxd - blue_mind) * 255. + .49999;
3485 col_int = FALSE;
3486 }
3487 set_colRGB24_param(rpar[i].def, cols[0], cols[1], cols[2]);
3488
3489 if (col_int) {
3490 lives_free(cols);
3491 cols = weed_get_int_array(wpar, WEED_LEAF_VALUE, NULL);
3492 if (cols[0] < red_min) cols[0] = red_min;
3493 if (cols[1] < green_min) cols[1] = green_min;
3494 if (cols[2] < blue_min) cols[2] = blue_min;
3495 if (cols[0] > red_max) cols[0] = red_max;
3496 if (cols[1] > green_max) cols[1] = green_max;
3497 if (cols[2] > blue_max) cols[2] = blue_max;
3498 cols[0] = (double)(cols[0] - red_min) / (double)(red_max - red_min) * 255. + .49999;
3499 cols[1] = (double)(cols[1] - green_min) / (double)(green_max - green_min) * 255. + .49999;
3500 cols[2] = (double)(cols[2] - blue_min) / (double)(blue_max - blue_min) * 255. + .49999;
3501 } else {
3502 colsd = weed_get_double_array(wpar, WEED_LEAF_VALUE, NULL);
3503 if (colsd[0] < red_mind) colsd[0] = red_mind;
3504 if (colsd[1] < green_mind) colsd[1] = green_mind;
3505 if (colsd[2] < blue_mind) colsd[2] = blue_mind;
3506 if (colsd[0] > red_maxd) colsd[0] = red_maxd;
3507 if (colsd[1] > green_maxd) colsd[1] = green_maxd;
3508 if (colsd[2] > blue_maxd) colsd[2] = blue_maxd;
3509 cols[0] = (colsd[0] - red_mind) / (red_maxd - red_mind) * 255. + .49999;
3510 cols[1] = (colsd[1] - green_mind) / (green_maxd - green_mind) * 255. + .49999;
3511 cols[2] = (colsd[2] - blue_mind) / (blue_maxd - blue_mind) * 255. + .49999;
3512 }
3513 set_colRGB24_param(rpar[i].value, (short)cols[0], (short)cols[1], (short)cols[2]);
3514 lives_free(cols);
3515
3516 lives_freep((void **)&maxi);
3517 lives_freep((void **)&mini);
3518 lives_freep((void **)&maxd);
3519 lives_freep((void **)&mind);
3520 break;
3521 }
3522 break;
3523
3524 default:
3525 rpar[i].type = LIVES_PARAM_UNKNOWN; // TODO - try to get default
3526 }
3527
3528 string = weed_get_string_value(wtmpl, WEED_LEAF_NAME, NULL);
3529 rpar[i].name = string;
3530 rpar[i].label = lives_strdup(string);
3531
3532 if (weed_plant_has_leaf(wtmpl, WEED_LEAF_DESCRIPTION)) {
3533 string = weed_get_string_value(wtmpl, WEED_LEAF_DESCRIPTION, NULL);
3534 rpar[i].desc = string;
3535 } else rpar[i].desc = NULL;
3536
3537 if (is_hidden_param(inst, i)) rpar[i].hidden |= HIDDEN_GUI_PERM;
3538
3539 // gui part /////////////////////
3540
3541 if (gui) {
3542 if (weed_plant_has_leaf(gui, WEED_LEAF_LABEL)) {
3543 string = weed_get_string_value(gui, WEED_LEAF_LABEL, NULL);
3544 lives_free(rpar[i].label);
3545 rpar[i].label = string;
3546 }
3547 if (weed_plant_has_leaf(gui, WEED_LEAF_USE_MNEMONIC)) {
3548 rpar[i].use_mnemonic = weed_get_boolean_value(gui, WEED_LEAF_USE_MNEMONIC, NULL);
3549 }
3550 }
3551
3552 for (j = 0; j < MAX_PARAM_WIDGETS; j++) {
3553 rpar[i].widgets[j] = NULL;
3554 }
3555 rpar[i].onchange = FALSE;
3556 }
3557
3558 lives_free(wpars);
3559
3560 return rpar;
3561 }
3562
3563
weed_to_rfx(weed_plant_t * plant,boolean show_reinits)3564 lives_rfx_t *weed_to_rfx(weed_plant_t *plant, boolean show_reinits) {
3565 // return an RFX for a weed effect; set rfx->source to an INSTANCE of the filter (first instance for compound fx)
3566 // instance should be refcounted
3567 weed_plant_t *filter, *inst;
3568
3569 char *string;
3570 lives_rfx_t *rfx = (lives_rfx_t *)lives_calloc(1, sizeof(lives_rfx_t));
3571 rfx->is_template = FALSE;
3572 if (weed_get_int_value(plant, WEED_LEAF_TYPE, NULL) == WEED_PLANT_FILTER_INSTANCE) {
3573 filter = weed_instance_get_filter(plant, TRUE);
3574 inst = plant;
3575 } else {
3576 filter = plant;
3577 inst = weed_instance_from_filter(filter);
3578 // init and deinit the effect to allow the plugin to hide parameters, etc.
3579 // rfx will inherit the refcount
3580 weed_reinit_effect(inst, TRUE);
3581 weed_instance_unref(inst);
3582 rfx->is_template = TRUE;
3583 }
3584
3585 string = weed_get_string_value(filter, WEED_LEAF_NAME, NULL);
3586 rfx->name = string;
3587 rfx->menu_text = lives_strdup(string);
3588 rfx->action_desc = lives_strdup("no action");
3589 rfx->min_frames = -1;
3590 rfx->num_in_channels = enabled_in_channels(filter, FALSE);
3591 rfx->status = RFX_STATUS_WEED;
3592 rfx->props = 0;
3593 rfx->menuitem = NULL;
3594 rfx->num_params = weed_leaf_num_elements(filter, WEED_LEAF_IN_PARAMETER_TEMPLATES);
3595 if (rfx->num_params > 0) rfx->params = weed_params_to_rfx(rfx->num_params, inst, show_reinits);
3596 else rfx->params = NULL;
3597 rfx->source = (void *)inst;
3598 rfx->source_type = LIVES_RFX_SOURCE_WEED;
3599 rfx->gui_strings = NULL;
3600 rfx->onchange_strings = NULL;
3601 rfx->flags = 0;
3602 rfx->needs_reinit = FALSE;
3603 return rfx;
3604 }
3605
3606 /**
3607 @brief get the interface hints set by a Weed filter in the filter_class.
3608
3609 for a compound effect we get the gui elements from each internal filter in sequence,
3610 inserting internal|nextfilter after each filter
3611
3612 - the filter MUST have set LAYOUT_SCHEME to RFX in the filter class.
3613 - it must have set the leaf RFX_DELIM with the string delimiter (and anything after the first character is ignored)
3614 - the layout must be set in the RFX_STRINGS array, using the delimiter
3615
3616 returns a LiVESList of the results
3617 */
get_external_window_hints(lives_rfx_t * rfx)3618 LiVESList *get_external_window_hints(lives_rfx_t *rfx) {
3619 LiVESList *hints = NULL;
3620
3621 if (rfx->status == RFX_STATUS_WEED) {
3622 weed_plant_t *gui;
3623 weed_plant_t *inst = (weed_plant_t *)rfx->source;
3624 weed_plant_t *filter = weed_instance_get_filter(inst, TRUE);
3625 int *filters = NULL;
3626 char *string, **rfx_strings, *delim;
3627 int nfilters;
3628 int num_hints;
3629 int i;
3630
3631 if ((nfilters = num_compound_fx(filter)) > 1) {
3632 // handle compound fx
3633 filters = weed_get_int_array(filter, WEED_LEAF_HOST_FILTER_LIST, NULL);
3634 }
3635
3636 for (i = 0; i < nfilters; i++) {
3637 if (filters) {
3638 filter = get_weed_filter(filters[i]);
3639 }
3640
3641 if (!weed_plant_has_leaf(filter, WEED_LEAF_GUI)) continue;
3642 gui = weed_get_plantptr_value(filter, WEED_LEAF_GUI, NULL);
3643
3644 if (!weed_plant_has_leaf(gui, WEED_LEAF_LAYOUT_SCHEME)) continue;
3645
3646 string = weed_get_string_value(gui, WEED_LEAF_LAYOUT_SCHEME, NULL);
3647 if (strcmp(string, "RFX")) {
3648 lives_free(string);
3649 continue;
3650 }
3651 lives_free(string);
3652
3653 if (!weed_plant_has_leaf(gui, WEED_LEAF_RFX_DELIM)) continue;
3654 delim = weed_get_string_value(gui, WEED_LEAF_RFX_DELIM, NULL);
3655 lives_snprintf(rfx->delim, 2, "%s", delim);
3656 lives_free(delim);
3657
3658 rfx_strings = weed_get_string_array_counted(gui, WEED_LEAF_RFX_STRINGS, &num_hints);
3659 if (!num_hints) continue;
3660
3661 for (i = 0; i < num_hints; i++) {
3662 hints = lives_list_append(hints, rfx_strings[i]);
3663 }
3664 lives_free(rfx_strings);
3665
3666 if (filters) hints = lives_list_append(hints, lives_strdup("internal|nextfilter"));
3667 }
3668
3669 lives_freep((void **)&filters);
3670 }
3671
3672 return hints;
3673 }
3674
3675
rfx_clean_exe(lives_rfx_t * rfx)3676 void rfx_clean_exe(lives_rfx_t *rfx) {
3677 if (rfx) {
3678 char *fnamex = lives_build_filename(prefs->workdir, rfx->name, NULL);
3679 if (lives_file_test(fnamex, LIVES_FILE_TEST_EXISTS)) lives_rm(fnamex);
3680 lives_free(fnamex);
3681 }
3682 }
3683
3684
3685 /**
3686 @brief create an interface window for a plugin; possibly run it, and return the parameters
3687
3688 N.B. this is NOT for rendered effects, those have their own functions.
3689
3690 -- currently used for: encoder plugins and video playback plugins.
3691
3692 Given an RFX script in scrap_text, (generally retrieved by some means from the plugin),
3693 will create an rfx effect, building the parameters from the <params> section of scrap_text,
3694 using the layout hints (optional) from <param_window>, and construct a parameter interface.
3695
3696 The function has two modes of operation:
3697
3698 If vbox is not NULL it should point to a LiVESVBox into which the parameter box will be added.
3699 The function will return NULL, and the rfx can be retrieved from ret_rfx.
3700
3701 If vbox is NULL, the param window will be run, and if the user clicks "OK", the parameter values are returned in a marshalled list.
3702 If the user closes the window with Cancel, NULL is returned instead.
3703
3704 If the plugin has no user adjustable parameters, the an empty string is returned.
3705
3706 If <onchange> exists then the init | trigger will be run
3707 to let the plugin update default values (for vpps only currently)
3708
3709 The onchange code is currently run by generating a perl scrap and runing that. In future the code could
3710 be run in different languages or internally by using a simple parser like the one in the data_processor plugin.
3711
3712
3713 NOTE: if vbox is not NULL, we create the window inside vbox, without running it
3714 in this case, vbox should be packed in its own dialog window, which should then be run
3715
3716 called from plugins.c (vpp opts) and saveplay.c (encoder opts) */
plugin_run_param_window(const char * scrap_text,LiVESVBox * vbox,lives_rfx_t ** ret_rfx)3717 char *plugin_run_param_window(const char *scrap_text, LiVESVBox * vbox, lives_rfx_t **ret_rfx) {
3718 FILE *sfile;
3719
3720 lives_rfx_t *rfx = (lives_rfx_t *)lives_calloc(1, sizeof(lives_rfx_t));
3721
3722 char *string;
3723 char *rfx_scrapname, *rfx_scriptname;
3724 char *rfxfile;
3725 char *com;
3726 char *fnamex = NULL;
3727 char *res_string = NULL;
3728 char buff[32];
3729
3730 int res;
3731 int retval;
3732
3733 if (!check_for_executable(&capable->has_mktemp, EXEC_MKTEMP)) {
3734 do_program_not_found_error(EXEC_MKTEMP);
3735 return NULL;
3736 }
3737
3738 rfx_scrapname = get_worktmpfile("rfx.");
3739 if (!rfx_scrapname) {
3740 workdir_warning();
3741 return NULL;
3742 }
3743
3744 rfx_scriptname = lives_strdup_printf("%s.%s", rfx_scrapname, LIVES_FILE_EXT_RFX_SCRIPT);
3745
3746 rfxfile = lives_build_path(prefs->workdir, rfx_scriptname, NULL);
3747 lives_free(rfx_scriptname);
3748
3749 rfx->name = NULL;
3750
3751 string = lives_strdup_printf("<name>\n%s\n</name>\n", rfx_scrapname);
3752
3753 do {
3754 retval = 0;
3755 sfile = fopen(rfxfile, "w");
3756 if (!sfile) {
3757 retval = do_write_failed_error_s_with_retry(rfxfile, lives_strerror(errno));
3758 if (retval == LIVES_RESPONSE_CANCEL) {
3759 lives_free(string);
3760 return NULL;
3761 }
3762 } else {
3763 THREADVAR(write_failed) = FALSE;
3764 lives_fputs(string, sfile);
3765 if (scrap_text) {
3766 char *data = subst(scrap_text, "\\n", "\n");
3767 lives_fputs(data, sfile);
3768 lives_free(data);
3769 }
3770 fclose(sfile);
3771 lives_free(string);
3772 if (THREADVAR(write_failed)) {
3773 retval = do_write_failed_error_s_with_retry(rfxfile, NULL);
3774 if (retval == LIVES_RESPONSE_CANCEL) {
3775 return NULL;
3776 }
3777 }
3778 }
3779 } while (retval == LIVES_RESPONSE_RETRY);
3780
3781 // OK, we should now have an RFX fragment in a file, we can compile it, then build a parameter window from it
3782
3783 // call RFX_BUILDER program to compile the script, passing parameters input_filename and output_directory
3784 com = lives_strdup_printf("\"%s\" \"%s\" \"%s\" >%s", EXEC_RFX_BUILDER, rfxfile, prefs->workdir, LIVES_DEVNULL);
3785 res = lives_system(com, TRUE);
3786 lives_free(com);
3787
3788 lives_rm(rfxfile);
3789 lives_free(rfxfile);
3790
3791 if (res == 0) {
3792 // the script compiled correctly
3793
3794 // now we pop up the parameter window, get the values of our parameters, and marshall them as extra_params
3795
3796 // first create a lives_rfx_t from the scrap
3797 rfx->name = lives_strdup(rfx_scrapname);
3798 rfx->menu_text = NULL;
3799 rfx->action_desc = NULL;
3800 rfx->gui_strings = NULL;
3801 rfx->onchange_strings = NULL;
3802 lives_snprintf(rfx->rfx_version, 64, "%s", RFX_VERSION);
3803 rfx->flags = 0;
3804 rfx->status = RFX_STATUS_SCRAP;
3805
3806 rfx->num_in_channels = 0;
3807 rfx->min_frames = -1;
3808
3809 rfx->flags = RFX_FLAGS_NO_SLIDERS;
3810
3811 fnamex = lives_build_filename(prefs->workdir, rfx_scrapname, NULL);
3812 com = lives_strdup_printf("\"%s\" get_define", fnamex);
3813 lives_free(fnamex);
3814
3815 if (!lives_popen(com, TRUE, buff, 32)) {
3816 THREADVAR(com_failed) = TRUE;
3817 }
3818 lives_free(com);
3819
3820 // command to get_define failed
3821 if (THREADVAR(com_failed)) {
3822 rfx_clean_exe(rfx);
3823 return NULL;
3824 }
3825
3826 lives_snprintf(rfx->delim, 2, "%s", buff);
3827
3828 // ok, this might need adjusting afterwards
3829 rfx->menu_text = (vbox == NULL ? lives_strdup_printf(_("%s advanced settings"), prefs->encoder.of_desc) : lives_strdup(""));
3830 rfx->is_template = FALSE;
3831
3832 rfx->source = NULL;
3833 rfx->source_type = LIVES_RFX_SOURCE_RFX;
3834
3835 #if 0
3836 render_fx_get_params(rfx, scrap_text, RFX_STATUS_INTERNAL);
3837 #else
3838 render_fx_get_params(rfx, rfx_scrapname, RFX_STATUS_SCRAP);
3839 #endif
3840
3841 /// check if we actually have params to display
3842 if (!make_param_box(NULL, rfx)) {
3843 res_string = lives_strdup("");
3844 goto prpw_done;
3845 }
3846
3847 // now we build our window and get param values
3848 if (!vbox) {
3849 _fx_dialog *fxdialog = on_fx_pre_activate(rfx, TRUE, NULL);
3850 LiVESWidget *dialog = fxdialog->dialog;
3851 if (prefs->show_gui) {
3852 lives_window_set_transient_for(LIVES_WINDOW(dialog), LIVES_WINDOW(LIVES_MAIN_WINDOW_WIDGET));
3853 }
3854 lives_window_set_modal(LIVES_WINDOW(dialog), TRUE);
3855
3856 do {
3857 res = lives_dialog_run(LIVES_DIALOG(dialog));
3858 } while (res == LIVES_RESPONSE_RETRY);
3859
3860 if (res == LIVES_RESPONSE_OK) {
3861 // marshall our params for passing to the plugin
3862 res_string = param_marshall(rfx, FALSE);
3863 }
3864
3865 lives_widget_destroy(dialog);
3866
3867 if (fx_dialog[1]) {
3868 lives_freep((void **)&fx_dialog[1]);
3869 }
3870 } else {
3871 make_param_box(vbox, rfx);
3872 }
3873
3874 prpw_done:
3875 if (ret_rfx) {
3876 *ret_rfx = rfx;
3877 } else {
3878 rfx_clean_exe(rfx);
3879 rfx_free(rfx);
3880 lives_free(rfx);
3881 }
3882 } else {
3883 if (ret_rfx) {
3884 *ret_rfx = NULL;
3885 } else {
3886 res_string = lives_strdup("");
3887 }
3888 if (rfx) {
3889 lives_free(rfx);
3890 }
3891 }
3892 lives_free(rfx_scrapname);
3893 return res_string;
3894 }
3895
3896