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
stop_video_server(bool ask_confirm)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 switch (confirm.run()) {
81 case RESPONSE_ACCEPT:
82 break;
83 default:
84 return;
85 }
86 }
87 delete video_server_process;
88 video_server_process =0;
89 }
90 }
91
92 void
start_video_server_menu(Gtk::Window * float_window)93 ARDOUR_UI::start_video_server_menu (Gtk::Window* float_window)
94 {
95 ARDOUR_UI::start_video_server( float_window, true);
96 }
97
98 bool
start_video_server(Gtk::Window * float_window,bool popup_msg)99 ARDOUR_UI::start_video_server (Gtk::Window* float_window, bool popup_msg)
100 {
101 if (!_session) {
102 return false;
103 }
104 if (popup_msg) {
105 if (ARDOUR_UI::instance()->video_timeline->check_server()) {
106 if (video_server_process) {
107 popup_error(_("The Video Server is already started."));
108 } else {
109 popup_error(_("An external Video Server is configured and can be reached. Not starting a new instance."));
110 }
111 }
112 }
113
114 int firsttime = 0;
115 while (!ARDOUR_UI::instance()->video_timeline->check_server()) {
116 if (firsttime++) {
117 warning << _("Could not connect to the Video Server. Start it or configure its access URL in Preferences.") << endmsg;
118 }
119 VideoServerDialog *video_server_dialog = new VideoServerDialog (_session);
120 if (float_window) {
121 video_server_dialog->set_transient_for (*float_window);
122 }
123
124 if (!Config->get_show_video_server_dialog() && firsttime < 2) {
125 video_server_dialog->hide();
126 } else {
127 ResponseType r = (ResponseType) video_server_dialog->run ();
128 video_server_dialog->hide();
129 if (r != RESPONSE_ACCEPT) { return false; }
130 if (video_server_dialog->show_again()) {
131 Config->set_show_video_server_dialog(false);
132 }
133 }
134
135 std::string icsd_exec = video_server_dialog->get_exec_path();
136 std::string icsd_docroot = video_server_dialog->get_docroot();
137 #ifndef PLATFORM_WINDOWS
138 if (icsd_docroot.empty()) {
139 icsd_docroot = VideoUtils::video_get_docroot (Config);
140 }
141 #endif
142
143 GStatBuf sb;
144 #ifdef PLATFORM_WINDOWS
145 if (VideoUtils::harvid_version >= 0x000802 && icsd_docroot.empty()) {
146 /* OK, allow all drive letters */
147 } else
148 #endif
149 if (g_lstat (icsd_docroot.c_str(), &sb) != 0 || !S_ISDIR(sb.st_mode)) {
150 warning << _("Specified docroot is not an existing directory.") << endmsg;
151 continue;
152 }
153 #ifndef PLATFORM_WINDOWS
154 if ( (g_lstat (icsd_exec.c_str(), &sb) != 0)
155 || (sb.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)) == 0 ) {
156 warning << _("Given Video Server is not an executable file.") << endmsg;
157 continue;
158 }
159 #else
160 if ( (g_lstat (icsd_exec.c_str(), &sb) != 0)
161 || (sb.st_mode & (S_IXUSR)) == 0 ) {
162 warning << _("Given Video Server is not an executable file.") << endmsg;
163 continue;
164 }
165 #endif
166
167 char **argp;
168 argp=(char**) calloc(9,sizeof(char*));
169 argp[0] = strdup(icsd_exec.c_str());
170 argp[1] = strdup("-P");
171 argp[2] = (char*) calloc(16,sizeof(char)); snprintf(argp[2], 16, "%s", video_server_dialog->get_listenaddr().c_str());
172 argp[3] = strdup("-p");
173 argp[4] = (char*) calloc(6,sizeof(char)); snprintf(argp[4], 6, "%i", video_server_dialog->get_listenport());
174 argp[5] = strdup("-C");
175 argp[6] = (char*) calloc(6,sizeof(char)); snprintf(argp[6], 6, "%i", video_server_dialog->get_cachesize());
176 argp[7] = strdup(icsd_docroot.c_str());
177 argp[8] = 0;
178 stop_video_server();
179
180 #ifdef PLATFORM_WINDOWS
181 if (VideoUtils::harvid_version >= 0x000802 && icsd_docroot.empty()) {
182 /* OK, allow all drive letters */
183 } else
184 #endif
185 if (icsd_docroot == X_("/") || icsd_docroot == X_("C:\\")) {
186 Config->set_video_advanced_setup(false);
187 } else {
188 std::string url_str = "http://127.0.0.1:" + to_string(video_server_dialog->get_listenport()) + "/";
189 Config->set_video_server_url(url_str);
190 Config->set_video_server_docroot(icsd_docroot);
191 Config->set_video_advanced_setup(true);
192 }
193
194 if (video_server_process) {
195 delete video_server_process;
196 }
197
198 video_server_process = new ARDOUR::SystemExec(icsd_exec, argp);
199 if (video_server_process->start()) {
200 warning << _("Cannot launch the video-server") << endmsg;
201 continue;
202 }
203 int timeout = 120; // 6 sec
204 while (!ARDOUR_UI::instance()->video_timeline->check_server()) {
205 Glib::usleep (50000);
206 gui_idle_handler();
207 if (--timeout <= 0 || !video_server_process->is_running()) break;
208 }
209 if (timeout <= 0) {
210 warning << _("Video-server was started but does not respond to requests...") << endmsg;
211 } else {
212 if (!ARDOUR_UI::instance()->video_timeline->check_server_docroot()) {
213 delete video_server_process;
214 video_server_process = 0;
215 }
216 }
217 }
218 return true;
219 }
220
221 void
add_video(Gtk::Window * float_window)222 ARDOUR_UI::add_video (Gtk::Window* float_window)
223 {
224 if (!_session) {
225 return;
226 }
227
228 if (!start_video_server(float_window, false)) {
229 warning << _("Could not connect to the Video Server. Start it or configure its access URL in Preferences.") << endmsg;
230 return;
231 }
232
233 if (float_window) {
234 add_video_dialog->set_transient_for (*float_window);
235 }
236
237 if (add_video_dialog->is_visible()) {
238 /* we're already doing this */
239 return;
240 }
241
242 ResponseType r = (ResponseType) add_video_dialog->run ();
243 add_video_dialog->hide();
244 if (r != RESPONSE_ACCEPT) { return; }
245
246 bool local_file, orig_local_file;
247 std::string path = add_video_dialog->file_name(local_file);
248
249 std::string orig_path = path;
250 orig_local_file = local_file;
251
252 bool auto_set_session_fps = add_video_dialog->auto_set_session_fps();
253
254 if (local_file && !Glib::file_test(path, Glib::FILE_TEST_EXISTS)) {
255 warning << string_compose(_("could not open %1"), path) << endmsg;
256 return;
257 }
258 if (!local_file && path.length() == 0) {
259 warning << _("no video-file selected") << endmsg;
260 return;
261 }
262
263 std::string audio_from_video;
264 bool detect_ltc = false;
265
266 switch (add_video_dialog->import_option()) {
267 case VTL_IMPORT_TRANSCODE:
268 {
269 TranscodeVideoDialog *transcode_video_dialog;
270 transcode_video_dialog = new TranscodeVideoDialog (_session, path);
271 ResponseType r = (ResponseType) transcode_video_dialog->run ();
272 transcode_video_dialog->hide();
273 if (r != RESPONSE_ACCEPT) {
274 delete transcode_video_dialog;
275 return;
276 }
277
278 audio_from_video = transcode_video_dialog->get_audiofile();
279
280 if (!audio_from_video.empty() && transcode_video_dialog->detect_ltc()) {
281 detect_ltc = true;
282 }
283 else if (!audio_from_video.empty()) {
284 editor->embed_audio_from_video(
285 audio_from_video,
286 video_timeline->get_offset(),
287 (transcode_video_dialog->import_option() != VTL_IMPORT_NO_VIDEO)
288 );
289 }
290 switch (transcode_video_dialog->import_option()) {
291 case VTL_IMPORT_TRANSCODED:
292 path = transcode_video_dialog->get_filename();
293 local_file = true;
294 break;
295 case VTL_IMPORT_REFERENCE:
296 break;
297 default:
298 delete transcode_video_dialog;
299 return;
300 }
301 delete transcode_video_dialog;
302 }
303 break;
304 default:
305 case VTL_IMPORT_NONE:
306 break;
307 }
308
309 /* strip _session->session_directory().video_path() from video file if possible */
310 if (local_file && !path.compare(0, _session->session_directory().video_path().size(), _session->session_directory().video_path())) {
311 path=path.substr(_session->session_directory().video_path().size());
312 if (path.at(0) == G_DIR_SEPARATOR) {
313 path=path.substr(1);
314 }
315 }
316
317 video_timeline->set_update_session_fps(auto_set_session_fps);
318
319 if (video_timeline->video_file_info(path, local_file)) {
320 XMLNode* node = new XMLNode(X_("Videotimeline"));
321 node->set_property (X_("Filename"), path);
322 node->set_property (X_("AutoFPS"), auto_set_session_fps);
323 node->set_property (X_("LocalFile"), local_file);
324 if (orig_local_file) {
325 node->set_property (X_("OriginalVideoFile"), orig_path);
326 } else {
327 node->remove_property (X_("OriginalVideoFile"));
328 }
329 _session->add_extra_xml (*node);
330 _session->set_dirty ();
331
332 if (!audio_from_video.empty() && detect_ltc) {
333 std::vector<LTCFileReader::LTCMap> ltc_seq;
334
335 try {
336 /* TODO ask user about TV standard (LTC alignment if any) */
337 LTCFileReader ltcr (audio_from_video, video_timeline->get_video_file_fps());
338 /* TODO ASK user which channel: 0 .. ltcr->channels() - 1 */
339
340 ltc_seq = ltcr.read_ltc (/*channel*/ 0, /*max LTC samples to decode*/ 15);
341
342 /* TODO seek near end of file, and read LTC until end.
343 * if it fails to find any LTC samples, scan complete file
344 *
345 * calculate drift of LTC compared to video-duration,
346 * ask user for reference (timecode from start/mid/end)
347 */
348 } catch (...) {
349 // LTCFileReader will have written error messages
350 }
351
352 ::g_unlink(audio_from_video.c_str());
353
354 if (ltc_seq.size() == 0) {
355 PBD::error << _("No LTC detected, video will not be aligned.") << endmsg;
356 } else {
357 /* the very first TC in the file is somteimes not aligned properly */
358 int i = ltc_seq.size() -1;
359 ARDOUR::sampleoffset_t video_start_offset =
360 _session->nominal_sample_rate() * (ltc_seq[i].timecode_sec - ltc_seq[i].framepos_sec);
361 PBD::info << string_compose (_("Align video-start to %1 [samples]"), video_start_offset) << endmsg;
362 video_timeline->set_offset(video_start_offset);
363 }
364 }
365
366 _session->maybe_update_session_range(
367 std::max(video_timeline->get_offset(), (ARDOUR::sampleoffset_t) 0),
368 std::max(video_timeline->get_offset() + video_timeline->get_duration(), (ARDOUR::sampleoffset_t) 0));
369
370
371 if (add_video_dialog->launch_xjadeo() && local_file) {
372 editor->set_xjadeo_sensitive(true);
373 editor->toggle_xjadeo_proc(1);
374 } else {
375 editor->toggle_xjadeo_proc(0);
376 }
377 editor->toggle_ruler_video(true);
378 }
379 }
380
381 void
remove_video()382 ARDOUR_UI::remove_video ()
383 {
384 video_timeline->close_session();
385 editor->toggle_ruler_video(false);
386
387 /* reset state */
388 video_timeline->set_offset_locked(false);
389 video_timeline->set_offset(0);
390
391 /* delete session state */
392 XMLNode* node = new XMLNode(X_("Videotimeline"));
393 _session->add_extra_xml(*node);
394 node = new XMLNode(X_("Videomonitor"));
395 _session->add_extra_xml(*node);
396 node = new XMLNode(X_("Videoexport"));
397 _session->add_extra_xml(*node);
398 stop_video_server();
399 }
400
401 void
flush_videotimeline_cache(bool localcacheonly)402 ARDOUR_UI::flush_videotimeline_cache (bool localcacheonly)
403 {
404 if (localcacheonly) {
405 video_timeline->vmon_update();
406 } else {
407 video_timeline->flush_cache();
408 }
409 editor->queue_visual_videotimeline_update();
410 }
411
412 void
export_video(bool range)413 ARDOUR_UI::export_video (bool range)
414 {
415 if (ARDOUR::Config->get_show_video_export_info()) {
416 ExportVideoInfobox infobox (_session);
417 Gtk::ResponseType rv = (Gtk::ResponseType) infobox.run();
418 if (infobox.show_again()) {
419 ARDOUR::Config->set_show_video_export_info(false);
420 }
421 switch (rv) {
422 case RESPONSE_YES:
423 PBD::open_uri (ARDOUR::Config->get_reference_manual_url() + "/video-timeline/operations/#export");
424 break;
425 default:
426 break;
427 }
428 }
429 export_video_dialog->set_session (_session);
430 export_video_dialog->apply_state(editor->get_selection().time, range);
431 export_video_dialog->run ();
432 export_video_dialog->hide ();
433 }
434