Fix DSP load sorting with inactive plugins
[ardour.git] / gtk2_ardour / ardour_ui_video.cc
1 /*
2  * Copyright (C) 2005-2007 Doug McLain <doug@nostar.net>
3  * Copyright (C) 2005-2017 Tim Mayberry <mojofunk@gmail.com>
4  * Copyright (C) 2005-2019 Paul Davis <paul@linuxaudiosystems.com>
5  * Copyright (C) 2005 Karsten Wiese <fzuuzf@googlemail.com>
6  * Copyright (C) 2005 Taybin Rutkin <taybin@taybin.com>
7  * Copyright (C) 2006-2015 David Robillard <d@drobilla.net>
8  * Copyright (C) 2007-2012 Carl Hetherington <carl@carlh.net>
9  * Copyright (C) 2008-2010 Sakari Bergen <sakari.bergen@beatwaves.net>
10  * Copyright (C) 2012-2019 Robin Gareus <robin@gareus.org>
11  * Copyright (C) 2013-2015 Colin Fletcher <colin.m.fletcher@googlemail.com>
12  * Copyright (C) 2013-2016 John Emmas <john@creativepost.co.uk>
13  * Copyright (C) 2013-2016 Nick Mainsbridge <mainsbridge@gmail.com>
14  * Copyright (C) 2014-2018 Ben Loftis <ben@harrisonconsoles.com>
15  * Copyright (C) 2015 AndrĂ© Nusser <andre.nusser@googlemail.com>
16  * Copyright (C) 2016-2018 Len Ovens <len@ovenwerks.net>
17  * Copyright (C) 2017 Johannes Mueller <github@johannes-mueller.org>
18  *
19  * This program is free software; you can redistribute it and/or modify
20  * it under the terms of the GNU General Public License as published by
21  * the Free Software Foundation; either version 2 of the License, or
22  * (at your option) any later version.
23  *
24  * This program is distributed in the hope that it will be useful,
25  * but WITHOUT ANY WARRANTY; without even the implied warranty of
26  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
27  * GNU General Public License for more details.
28  *
29  * You should have received a copy of the GNU General Public License along
30  * with this program; if not, write to the Free Software Foundation, Inc.,
31  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
32  */
33
34 #ifdef WAF_BUILD
35 #include "gtk2ardour-config.h"
36 #include "gtk2ardour-version.h"
37 #endif
38
39 #include "pbd/gstdio_compat.h"
40
41 #include <gtkmm/stock.h>
42
43 #include "pbd/error.h"
44 #include "pbd/openuri.h"
45
46 #include "ardour/ltc_file_reader.h"
47 #include "ardour/session_directory.h"
48
49 #include "add_video_dialog.h"
50 #include "ardour_ui.h"
51 #include "export_video_infobox.h"
52 #include "export_video_dialog.h"
53 #include "public_editor.h"
54 #include "utils_videotl.h"
55 #include "transcode_video_dialog.h"
56 #include "video_server_dialog.h"
57
58 #include "pbd/i18n.h"
59
60 using namespace ARDOUR;
61 using namespace PBD;
62 using namespace Gtk;
63 using namespace Gtkmm2ext;
64 using namespace std;
65
66 void
67 ARDOUR_UI::stop_video_server (bool ask_confirm)
68 {
69         if (!video_server_process && ask_confirm) {
70                 warning << string_compose (_("Video-Server was not launched by %1. The request to stop it is ignored."), PROGRAM_NAME) << endmsg;
71         }
72         if (video_server_process) {
73                 if(ask_confirm) {
74                         ArdourDialog confirm (_("Stop Video-Server"), true);
75                         Label m (_("Do you really want to stop the Video Server?"));
76                         confirm.get_vbox()->pack_start (m, true, true);
77                         confirm.add_button (Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
78                         confirm.add_button (_("Yes, Stop It"), Gtk::RESPONSE_ACCEPT);
79                         confirm.show_all ();
80                         if (confirm.run() == RESPONSE_CANCEL) {
81                                 return;
82                         }
83                 }
84                 delete video_server_process;
85                 video_server_process =0;
86         }
87 }
88
89 void
90 ARDOUR_UI::start_video_server_menu (Gtk::Window* float_window)
91 {
92   ARDOUR_UI::start_video_server( float_window, true);
93 }
94
95 bool
96 ARDOUR_UI::start_video_server (Gtk::Window* float_window, bool popup_msg)
97 {
98         if (!_session) {
99                 return false;
100         }
101         if (popup_msg) {
102                 if (ARDOUR_UI::instance()->video_timeline->check_server()) {
103                         if (video_server_process) {
104                                 popup_error(_("The Video Server is already started."));
105                         } else {
106                                 popup_error(_("An external Video Server is configured and can be reached. Not starting a new instance."));
107                         }
108                 }
109         }
110
111         int firsttime = 0;
112         while (!ARDOUR_UI::instance()->video_timeline->check_server()) {
113                 if (firsttime++) {
114                         warning << _("Could not connect to the Video Server. Start it or configure its access URL in Preferences.") << endmsg;
115                 }
116                 VideoServerDialog *video_server_dialog = new VideoServerDialog (_session);
117                 if (float_window) {
118                         video_server_dialog->set_transient_for (*float_window);
119                 }
120
121                 if (!Config->get_show_video_server_dialog() && firsttime < 2) {
122                         video_server_dialog->hide();
123                 } else {
124                         ResponseType r = (ResponseType) video_server_dialog->run ();
125                         video_server_dialog->hide();
126                         if (r != RESPONSE_ACCEPT) { return false; }
127                         if (video_server_dialog->show_again()) {
128                                 Config->set_show_video_server_dialog(false);
129                         }
130                 }
131
132                 std::string icsd_exec = video_server_dialog->get_exec_path();
133                 std::string icsd_docroot = video_server_dialog->get_docroot();
134 #ifndef PLATFORM_WINDOWS
135                 if (icsd_docroot.empty()) {
136                         icsd_docroot = VideoUtils::video_get_docroot (Config);
137                 }
138 #endif
139
140                 GStatBuf sb;
141 #ifdef PLATFORM_WINDOWS
142                 if (VideoUtils::harvid_version >= 0x000802 && icsd_docroot.empty()) {
143                         /* OK, allow all drive letters */
144                 } else
145 #endif
146                 if (g_lstat (icsd_docroot.c_str(), &sb) != 0 || !S_ISDIR(sb.st_mode)) {
147                         warning << _("Specified docroot is not an existing directory.") << endmsg;
148                         continue;
149                 }
150 #ifndef PLATFORM_WINDOWS
151                 if ( (g_lstat (icsd_exec.c_str(), &sb) != 0)
152                      || (sb.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)) == 0 ) {
153                         warning << _("Given Video Server is not an executable file.") << endmsg;
154                         continue;
155                 }
156 #else
157                 if ( (g_lstat (icsd_exec.c_str(), &sb) != 0)
158                      || (sb.st_mode & (S_IXUSR)) == 0 ) {
159                         warning << _("Given Video Server is not an executable file.") << endmsg;
160                         continue;
161                 }
162 #endif
163
164                 char **argp;
165                 argp=(char**) calloc(9,sizeof(char*));
166                 argp[0] = strdup(icsd_exec.c_str());
167                 argp[1] = strdup("-P");
168                 argp[2] = (char*) calloc(16,sizeof(char)); snprintf(argp[2], 16, "%s", video_server_dialog->get_listenaddr().c_str());
169                 argp[3] = strdup("-p");
170                 argp[4] = (char*) calloc(6,sizeof(char)); snprintf(argp[4], 6, "%i", video_server_dialog->get_listenport());
171                 argp[5] = strdup("-C");
172                 argp[6] = (char*) calloc(6,sizeof(char)); snprintf(argp[6], 6, "%i", video_server_dialog->get_cachesize());
173                 argp[7] = strdup(icsd_docroot.c_str());
174                 argp[8] = 0;
175                 stop_video_server();
176
177 #ifdef PLATFORM_WINDOWS
178                 if (VideoUtils::harvid_version >= 0x000802 && icsd_docroot.empty()) {
179                         /* OK, allow all drive letters */
180                 } else
181 #endif
182                 if (icsd_docroot == X_("/") || icsd_docroot == X_("C:\\")) {
183                         Config->set_video_advanced_setup(false);
184                 } else {
185                         std::string url_str = "http://127.0.0.1:" + to_string(video_server_dialog->get_listenport()) + "/";
186                         Config->set_video_server_url(url_str);
187                         Config->set_video_server_docroot(icsd_docroot);
188                         Config->set_video_advanced_setup(true);
189                 }
190
191                 if (video_server_process) {
192                         delete video_server_process;
193                 }
194
195                 video_server_process = new ARDOUR::SystemExec(icsd_exec, argp);
196                 if (video_server_process->start()) {
197                         warning << _("Cannot launch the video-server") << endmsg;
198                         continue;
199                 }
200                 int timeout = 120; // 6 sec
201                 while (!ARDOUR_UI::instance()->video_timeline->check_server()) {
202                         Glib::usleep (50000);
203                         gui_idle_handler();
204                         if (--timeout <= 0 || !video_server_process->is_running()) break;
205                 }
206                 if (timeout <= 0) {
207                         warning << _("Video-server was started but does not respond to requests...") << endmsg;
208                 } else {
209                         if (!ARDOUR_UI::instance()->video_timeline->check_server_docroot()) {
210                                 delete video_server_process;
211                                 video_server_process = 0;
212                         }
213                 }
214         }
215         return true;
216 }
217
218 void
219 ARDOUR_UI::add_video (Gtk::Window* float_window)
220 {
221         if (!_session) {
222                 return;
223         }
224
225         if (!start_video_server(float_window, false)) {
226                 warning << _("Could not connect to the Video Server. Start it or configure its access URL in Preferences.") << endmsg;
227                 return;
228         }
229
230         if (float_window) {
231                 add_video_dialog->set_transient_for (*float_window);
232         }
233
234         if (add_video_dialog->is_visible()) {
235                 /* we're already doing this */
236                 return;
237         }
238
239         ResponseType r = (ResponseType) add_video_dialog->run ();
240         add_video_dialog->hide();
241         if (r != RESPONSE_ACCEPT) { return; }
242
243         bool local_file, orig_local_file;
244         std::string path = add_video_dialog->file_name(local_file);
245
246         std::string orig_path = path;
247         orig_local_file = local_file;
248
249         bool auto_set_session_fps = add_video_dialog->auto_set_session_fps();
250
251         if (local_file && !Glib::file_test(path, Glib::FILE_TEST_EXISTS)) {
252                 warning << string_compose(_("could not open %1"), path) << endmsg;
253                 return;
254         }
255         if (!local_file && path.length() == 0) {
256                 warning << _("no video-file selected") << endmsg;
257                 return;
258         }
259
260         std::string audio_from_video;
261         bool detect_ltc = false;
262
263         switch (add_video_dialog->import_option()) {
264                 case VTL_IMPORT_TRANSCODE:
265                         {
266                                 TranscodeVideoDialog *transcode_video_dialog;
267                                 transcode_video_dialog = new TranscodeVideoDialog (_session, path);
268                                 ResponseType r = (ResponseType) transcode_video_dialog->run ();
269                                 transcode_video_dialog->hide();
270                                 if (r != RESPONSE_ACCEPT) {
271                                         delete transcode_video_dialog;
272                                         return;
273                                 }
274
275                                 audio_from_video = transcode_video_dialog->get_audiofile();
276
277                                 if (!audio_from_video.empty() && transcode_video_dialog->detect_ltc()) {
278                                         detect_ltc = true;
279                                 }
280                                 else if (!audio_from_video.empty()) {
281                                         editor->embed_audio_from_video(
282                                                         audio_from_video,
283                                                         video_timeline->get_offset(),
284                                                         (transcode_video_dialog->import_option() != VTL_IMPORT_NO_VIDEO)
285                                                         );
286                                 }
287                                 switch (transcode_video_dialog->import_option()) {
288                                         case VTL_IMPORT_TRANSCODED:
289                                                 path = transcode_video_dialog->get_filename();
290                                                 local_file = true;
291                                                 break;
292                                         case VTL_IMPORT_REFERENCE:
293                                                 break;
294                                         default:
295                                                 delete transcode_video_dialog;
296                                                 return;
297                                 }
298                                 delete transcode_video_dialog;
299                         }
300                         break;
301                 default:
302                 case VTL_IMPORT_NONE:
303                         break;
304         }
305
306         /* strip _session->session_directory().video_path() from video file if possible */
307         if (local_file && !path.compare(0, _session->session_directory().video_path().size(), _session->session_directory().video_path())) {
308                  path=path.substr(_session->session_directory().video_path().size());
309                  if (path.at(0) == G_DIR_SEPARATOR) {
310                          path=path.substr(1);
311                  }
312         }
313
314         video_timeline->set_update_session_fps(auto_set_session_fps);
315
316         if (video_timeline->video_file_info(path, local_file)) {
317                 XMLNode* node = new XMLNode(X_("Videotimeline"));
318                 node->set_property (X_("Filename"), path);
319                 node->set_property (X_("AutoFPS"), auto_set_session_fps);
320                 node->set_property (X_("LocalFile"), local_file);
321                 if (orig_local_file) {
322                         node->set_property (X_("OriginalVideoFile"), orig_path);
323                 } else {
324                         node->remove_property (X_("OriginalVideoFile"));
325                 }
326                 _session->add_extra_xml (*node);
327                 _session->set_dirty ();
328
329                 if (!audio_from_video.empty() && detect_ltc) {
330                         std::vector<LTCFileReader::LTCMap> ltc_seq;
331
332                         try {
333                                 /* TODO ask user about TV standard (LTC alignment if any) */
334                                 LTCFileReader ltcr (audio_from_video, video_timeline->get_video_file_fps());
335                                 /* TODO ASK user which channel:  0 .. ltcr->channels() - 1 */
336
337                                 ltc_seq = ltcr.read_ltc (/*channel*/ 0, /*max LTC samples to decode*/ 15);
338
339                                 /* TODO seek near end of file, and read LTC until end.
340                                  * if it fails to find any LTC samples, scan complete file
341                                  *
342                                  * calculate drift of LTC compared to video-duration,
343                                  * ask user for reference (timecode from start/mid/end)
344                                  */
345                         } catch (...) {
346                                 // LTCFileReader will have written error messages
347                         }
348
349                         ::g_unlink(audio_from_video.c_str());
350
351                         if (ltc_seq.size() == 0) {
352                                 PBD::error << _("No LTC detected, video will not be aligned.") << endmsg;
353                         } else {
354                                 /* the very first TC in the file is somteimes not aligned properly */
355                                 int i = ltc_seq.size() -1;
356                                 ARDOUR::sampleoffset_t video_start_offset =
357                                         _session->nominal_sample_rate() * (ltc_seq[i].timecode_sec - ltc_seq[i].framepos_sec);
358                                 PBD::info << string_compose (_("Align video-start to %1 [samples]"), video_start_offset) << endmsg;
359                                 video_timeline->set_offset(video_start_offset);
360                         }
361                 }
362
363                 _session->maybe_update_session_range(
364                         std::max(video_timeline->get_offset(), (ARDOUR::sampleoffset_t) 0),
365                         std::max(video_timeline->get_offset() + video_timeline->get_duration(), (ARDOUR::sampleoffset_t) 0));
366
367
368                 if (add_video_dialog->launch_xjadeo() && local_file) {
369                         editor->set_xjadeo_sensitive(true);
370                         editor->toggle_xjadeo_proc(1);
371                 } else {
372                         editor->toggle_xjadeo_proc(0);
373                 }
374                 editor->toggle_ruler_video(true);
375         }
376 }
377
378 void
379 ARDOUR_UI::remove_video ()
380 {
381         video_timeline->close_session();
382         editor->toggle_ruler_video(false);
383
384         /* reset state */
385         video_timeline->set_offset_locked(false);
386         video_timeline->set_offset(0);
387
388         /* delete session state */
389         XMLNode* node = new XMLNode(X_("Videotimeline"));
390         _session->add_extra_xml(*node);
391         node = new XMLNode(X_("Videomonitor"));
392         _session->add_extra_xml(*node);
393         node = new XMLNode(X_("Videoexport"));
394         _session->add_extra_xml(*node);
395         stop_video_server();
396 }
397
398 void
399 ARDOUR_UI::flush_videotimeline_cache (bool localcacheonly)
400 {
401         if (localcacheonly) {
402                 video_timeline->vmon_update();
403         } else {
404                 video_timeline->flush_cache();
405         }
406         editor->queue_visual_videotimeline_update();
407 }
408
409 void
410 ARDOUR_UI::export_video (bool range)
411 {
412         if (ARDOUR::Config->get_show_video_export_info()) {
413                 ExportVideoInfobox infobox (_session);
414                 Gtk::ResponseType rv = (Gtk::ResponseType) infobox.run();
415                 if (infobox.show_again()) {
416                         ARDOUR::Config->set_show_video_export_info(false);
417                 }
418                 switch (rv) {
419                         case GTK_RESPONSE_YES:
420                                 PBD::open_uri (ARDOUR::Config->get_reference_manual_url() + "/video-timeline/operations/#export");
421                                 break;
422                         default:
423                                 break;
424                 }
425         }
426         export_video_dialog->set_session (_session);
427         export_video_dialog->apply_state(editor->get_selection().time, range);
428         export_video_dialog->run ();
429         export_video_dialog->hide ();
430 }