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>
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.
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.
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.
35 #include "gtk2ardour-config.h"
36 #include "gtk2ardour-version.h"
39 #include "pbd/gstdio_compat.h"
41 #include <gtkmm/stock.h>
43 #include "pbd/error.h"
44 #include "pbd/openuri.h"
46 #include "ardour/ltc_file_reader.h"
47 #include "ardour/session_directory.h"
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"
60 using namespace ARDOUR;
63 using namespace Gtkmm2ext;
67 ARDOUR_UI::stop_video_server (bool ask_confirm)
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;
72 if (video_server_process) {
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);
80 if (confirm.run() == RESPONSE_CANCEL) {
84 delete video_server_process;
85 video_server_process =0;
90 ARDOUR_UI::start_video_server_menu (Gtk::Window* float_window)
92 ARDOUR_UI::start_video_server( float_window, true);
96 ARDOUR_UI::start_video_server (Gtk::Window* float_window, bool 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."));
106 popup_error(_("An external Video Server is configured and can be reached. Not starting a new instance."));
112 while (!ARDOUR_UI::instance()->video_timeline->check_server()) {
114 warning << _("Could not connect to the Video Server. Start it or configure its access URL in Preferences.") << endmsg;
116 VideoServerDialog *video_server_dialog = new VideoServerDialog (_session);
118 video_server_dialog->set_transient_for (*float_window);
121 if (!Config->get_show_video_server_dialog() && firsttime < 2) {
122 video_server_dialog->hide();
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);
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);
141 #ifdef PLATFORM_WINDOWS
142 if (VideoUtils::harvid_version >= 0x000802 && icsd_docroot.empty()) {
143 /* OK, allow all drive letters */
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;
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;
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;
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());
177 #ifdef PLATFORM_WINDOWS
178 if (VideoUtils::harvid_version >= 0x000802 && icsd_docroot.empty()) {
179 /* OK, allow all drive letters */
182 if (icsd_docroot == X_("/") || icsd_docroot == X_("C:\\")) {
183 Config->set_video_advanced_setup(false);
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);
191 if (video_server_process) {
192 delete video_server_process;
195 video_server_process = new ARDOUR::SystemExec(icsd_exec, argp);
196 if (video_server_process->start()) {
197 warning << _("Cannot launch the video-server") << endmsg;
200 int timeout = 120; // 6 sec
201 while (!ARDOUR_UI::instance()->video_timeline->check_server()) {
202 Glib::usleep (50000);
204 if (--timeout <= 0 || !video_server_process->is_running()) break;
207 warning << _("Video-server was started but does not respond to requests...") << endmsg;
209 if (!ARDOUR_UI::instance()->video_timeline->check_server_docroot()) {
210 delete video_server_process;
211 video_server_process = 0;
219 ARDOUR_UI::add_video (Gtk::Window* float_window)
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;
231 add_video_dialog->set_transient_for (*float_window);
234 if (add_video_dialog->is_visible()) {
235 /* we're already doing this */
239 ResponseType r = (ResponseType) add_video_dialog->run ();
240 add_video_dialog->hide();
241 if (r != RESPONSE_ACCEPT) { return; }
243 bool local_file, orig_local_file;
244 std::string path = add_video_dialog->file_name(local_file);
246 std::string orig_path = path;
247 orig_local_file = local_file;
249 bool auto_set_session_fps = add_video_dialog->auto_set_session_fps();
251 if (local_file && !Glib::file_test(path, Glib::FILE_TEST_EXISTS)) {
252 warning << string_compose(_("could not open %1"), path) << endmsg;
255 if (!local_file && path.length() == 0) {
256 warning << _("no video-file selected") << endmsg;
260 std::string audio_from_video;
261 bool detect_ltc = false;
263 switch (add_video_dialog->import_option()) {
264 case VTL_IMPORT_TRANSCODE:
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;
275 audio_from_video = transcode_video_dialog->get_audiofile();
277 if (!audio_from_video.empty() && transcode_video_dialog->detect_ltc()) {
280 else if (!audio_from_video.empty()) {
281 editor->embed_audio_from_video(
283 video_timeline->get_offset(),
284 (transcode_video_dialog->import_option() != VTL_IMPORT_NO_VIDEO)
287 switch (transcode_video_dialog->import_option()) {
288 case VTL_IMPORT_TRANSCODED:
289 path = transcode_video_dialog->get_filename();
292 case VTL_IMPORT_REFERENCE:
295 delete transcode_video_dialog;
298 delete transcode_video_dialog;
302 case VTL_IMPORT_NONE:
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) {
314 video_timeline->set_update_session_fps(auto_set_session_fps);
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);
324 node->remove_property (X_("OriginalVideoFile"));
326 _session->add_extra_xml (*node);
327 _session->set_dirty ();
329 if (!audio_from_video.empty() && detect_ltc) {
330 std::vector<LTCFileReader::LTCMap> ltc_seq;
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 */
337 ltc_seq = ltcr.read_ltc (/*channel*/ 0, /*max LTC samples to decode*/ 15);
339 /* TODO seek near end of file, and read LTC until end.
340 * if it fails to find any LTC samples, scan complete file
342 * calculate drift of LTC compared to video-duration,
343 * ask user for reference (timecode from start/mid/end)
346 // LTCFileReader will have written error messages
349 ::g_unlink(audio_from_video.c_str());
351 if (ltc_seq.size() == 0) {
352 PBD::error << _("No LTC detected, video will not be aligned.") << endmsg;
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);
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));
368 if (add_video_dialog->launch_xjadeo() && local_file) {
369 editor->set_xjadeo_sensitive(true);
370 editor->toggle_xjadeo_proc(1);
372 editor->toggle_xjadeo_proc(0);
374 editor->toggle_ruler_video(true);
379 ARDOUR_UI::remove_video ()
381 video_timeline->close_session();
382 editor->toggle_ruler_video(false);
385 video_timeline->set_offset_locked(false);
386 video_timeline->set_offset(0);
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);
399 ARDOUR_UI::flush_videotimeline_cache (bool localcacheonly)
401 if (localcacheonly) {
402 video_timeline->vmon_update();
404 video_timeline->flush_cache();
406 editor->queue_visual_videotimeline_update();
410 ARDOUR_UI::export_video (bool range)
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);
419 case GTK_RESPONSE_YES:
420 PBD::open_uri (ARDOUR::Config->get_reference_manual_url() + "/video-timeline/operations/#export");
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 ();