ardour/gtk2_ardour/video_timeline.cc
Robin Gareus 4050ca5633
Update GPL boilerplate and (C)
Copyright-holder and year information is extracted from git log.

git history begins in 2005. So (C) from 1998..2005 is lost. Also some
(C) assignment of commits where the committer didn't use --author.
2019-08-03 15:53:15 +02:00

926 lines
27 KiB
C++

/*
* Copyright (C) 2013-2016 Tim Mayberry <mojofunk@gmail.com>
* Copyright (C) 2013-2018 Paul Davis <paul@linuxaudiosystems.com>
* Copyright (C) 2013-2019 Robin Gareus <robin@gareus.org>
* Copyright (C) 2013 John Emmas <john@creativepost.co.uk>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <algorithm>
#include <sigc++/bind.h>
#include "ardour/tempo.h"
#include "pbd/file_utils.h"
#include "pbd/types_convert.h"
#include "ardour/filesystem_paths.h"
#include "ardour/session_directory.h"
#include "ardour_ui.h"
#include "ardour_http.h"
#include "public_editor.h"
#include "gui_thread.h"
#include "utils_videotl.h"
#include "rgb_macros.h"
#include "video_timeline.h"
#include <gtkmm2ext/utils.h>
#include <pthread.h>
#include <curl/curl.h>
#include "pbd/i18n.h"
using namespace std;
using namespace ARDOUR;
using namespace PBD;
using namespace Timecode;
using namespace VideoUtils;
VideoTimeLine::VideoTimeLine (PublicEditor *ed, ArdourCanvas::Container *vbg, int initial_height)
: editor (ed)
, videotl_group(vbg)
, bar_height(initial_height)
{
video_start_offset = 0L;
video_offset = 0L;
video_offset_p = 0L;
video_duration = 0L;
auto_set_session_fps = false;
video_offset_lock = false;
video_aspect_ratio = 4.0/3.0;
Config->ParameterChanged.connect (*this, invalidator (*this), ui_bind (&VideoTimeLine::parameter_changed, this, _1), gui_context());
video_filename = "";
local_file = true;
video_file_fps = 25.0;
_flush_frames = false;
vmonitor=0;
reopen_vmonitor=false;
find_xjadeo();
find_harvid();
video_server_url = video_get_server_url(Config);
server_docroot = video_get_docroot(Config);
VtlUpdate.connect (*this, invalidator (*this), boost::bind (&PublicEditor::queue_visual_videotimeline_update, editor), gui_context());
GuiUpdate.connect (*this, invalidator (*this), boost::bind (&VideoTimeLine::gui_update, this, _1), gui_context());
}
VideoTimeLine::~VideoTimeLine ()
{
close_session();
}
/* close and save settings */
void
VideoTimeLine::save_session ()
{
if (!_session) {
return;
}
XMLNode* node = new XMLNode(X_("Videomonitor"));
if (!node) return;
node->set_property (X_("active"), (vmonitor && vmonitor->is_started()));
_session->add_extra_xml (*node);
if (vmonitor) {
if (vmonitor->is_started()) {
vmonitor->query_full_state(true);
}
vmonitor->save_session();
}
/* VTL settings */
node = _session->extra_xml (X_("Videotimeline"));
if (!node) return;
node->set_property (X_("id"), id());
node->set_property (X_("Height"), editor->get_videotl_bar_height());
node->set_property (X_("VideoOffsetLock"), video_offset_lock);
node->set_property (X_("VideoOffset"), video_offset);
node->set_property (X_("AutoFPS"), auto_set_session_fps);
}
/* close and save settings */
void
VideoTimeLine::close_session ()
{
if (video_duration == 0) {
return;
}
sessionsave.disconnect();
close_video_monitor();
remove_frames ();
video_filename = "";
video_duration = 0;
GuiUpdate("set-xjadeo-sensitive-off");
GuiUpdate("video-unavailable");
}
void
VideoTimeLine::sync_session_state ()
{
if (!_session || !vmonitor || !vmonitor->is_started()) {
return;
}
save_session();
}
/** load settings from session */
void
VideoTimeLine::set_session (ARDOUR::Session *s)
{
SessionHandlePtr::set_session (s);
if (!_session) { return ; }
_session->SessionSaveUnderway.connect_same_thread (sessionsave, boost::bind (&VideoTimeLine::save_session, this));
XMLNode* node = _session->extra_xml (X_("Videotimeline"));
if (!node || !node->property (X_("Filename"))) {
return;
}
ARDOUR_UI::instance()->start_video_server((Gtk::Window*)0, false);
set_id(*node);
int height;
if (node->get_property (X_("Height"), height)) {
editor->set_video_timeline_height(height);
}
#if 0 /* TODO THINK: set FPS first time only ?! */
XMLProperty const * propasfps = node->property (X_("AutoFPS"));
if (propasfps) {
auto_set_session_fps = atoi(propasfps->value())?true:false;
}
#endif
if (node->get_property (X_("VideoOffset"), video_offset)) {
video_offset_p = video_offset;
}
node->get_property (X_("VideoOffsetLock"), video_offset_lock);
node->get_property (X_("LocalFile"), local_file);
std::string filename;
if (node->get_property (X_("Filename"), filename)) {
video_file_info (filename, local_file);
}
if ((node = _session->extra_xml (X_("Videomonitor")))) {
bool active;
if (node->get_property (X_("active"), active) && active && found_xjadeo () &&
!video_filename.empty () && local_file) {
open_video_monitor();
}
}
_session->register_with_memento_command_factory(id(), this);
_session->config.ParameterChanged.connect (*this, invalidator (*this), ui_bind (&VideoTimeLine::parameter_changed, this, _1), gui_context());
}
void
VideoTimeLine::set_offset_locked (bool v) {
if (_session && v != video_offset_lock) {
_session->set_dirty ();
}
video_offset_lock = v;
}
void
VideoTimeLine::toggle_offset_locked () {
video_offset_lock = !video_offset_lock;
if (_session) {
_session->set_dirty ();
}
}
void
VideoTimeLine::save_undo ()
{
if (_session && video_offset_p != video_offset) {
_session->set_dirty ();
}
video_offset_p = video_offset;
}
int
VideoTimeLine::set_state (const XMLNode& node, int /*version*/)
{
node.get_property (X_("VideoOffset"), video_offset);
ARDOUR_UI::instance()->flush_videotimeline_cache(true);
return 0;
}
XMLNode&
VideoTimeLine::get_state ()
{
XMLNode* node = new XMLNode (X_("Videotimeline"));
node->set_property (X_("VideoOffset"), video_offset_p);
return *node;
}
void
VideoTimeLine::remove_frames ()
{
for (VideoFrames::iterator i = video_frames.begin(); i != video_frames.end(); ++i ) {
VideoImageFrame* frame = (*i);
delete frame;
(*i) = 0;
}
video_frames.clear();
}
VideoImageFrame*
VideoTimeLine::get_video_frame (samplepos_t vfn, int cut, int rightend)
{
if (vfn==0) cut=0;
for (VideoFrames::iterator i = video_frames.begin(); i != video_frames.end(); ++i) {
VideoImageFrame* frame = (*i);
if (abs(frame->get_video_frame_number()-vfn)<=cut
&& frame->get_rightend() == rightend) { return frame; }
}
return 0;
}
float
VideoTimeLine::get_apv()
{
// XXX: dup code - TODO use this fn in update_video_timeline()
float apv = -1; /* audio samples per video frame; */
if (!_session) return apv;
if (_session->config.get_use_video_file_fps()) {
if (video_file_fps == 0 ) return apv;
} else {
if (_session->timecode_frames_per_second() == 0 ) return apv;
}
if (_session->config.get_videotimeline_pullup()) {
apv = _session->sample_rate();
} else {
apv = _session->nominal_sample_rate();
}
if (_session->config.get_use_video_file_fps()) {
apv /= video_file_fps;
} else {
apv /= _session->timecode_frames_per_second();
}
return apv;
}
void
VideoTimeLine::update_video_timeline()
{
if (!_session) return;
if (_session->config.get_use_video_file_fps()) {
if (video_file_fps == 0 ) return;
} else {
if (_session->timecode_frames_per_second() == 0 ) return;
}
const double samples_per_pixel = editor->get_current_zoom();
const samplepos_t leftmost_sample = editor->leftmost_sample();
/* Outline:
* 1) calculate how many frames there should be in current zoom (plus 1 page on each side)
* 2) calculate first frame and distance between video-frames (according to zoom)
* 3) destroy/add frames
* 4) reposition existing frames
* 5) assign framenumber to frames -> request/decode video.
*/
/* video-file and session properties */
double display_vframe_width; /* unit: pixels ; width of one thumbnail in the timeline */
float apv; /* audio samples per video frame; */
samplepos_t leftmost_video_frame; /* unit: video-frame number ; temporary var -> vtl_start */
/* variables needed to render videotimeline -- what needs to computed first */
samplepos_t vtl_start; /* unit: audio-samples ; first displayed video-frame */
samplepos_t vtl_dist; /* unit: audio-samples ; distance between displayed video-frames */
unsigned int visible_video_frames; /* number of frames that fit on current canvas */
if (_session->config.get_videotimeline_pullup()) {
apv = _session->sample_rate();
} else {
apv = _session->nominal_sample_rate();
}
if (_session->config.get_use_video_file_fps()) {
apv /= video_file_fps;
} else {
apv /= _session->timecode_frames_per_second();
}
display_vframe_width = bar_height * video_aspect_ratio;
if (apv > samples_per_pixel * display_vframe_width) {
/* high-zoom: need space between successive video-frames */
vtl_dist = rint(apv);
} else {
/* continous timeline: skip video-frames */
vtl_dist = ceil(display_vframe_width * samples_per_pixel / apv) * apv;
}
assert (vtl_dist > 0);
assert (apv > 0);
leftmost_video_frame = floor (floor((long double)(leftmost_sample - video_start_offset - video_offset ) / vtl_dist) * vtl_dist / apv);
vtl_start = rint (video_offset + video_start_offset + leftmost_video_frame * apv);
visible_video_frames = 2 + ceil((double)editor->current_page_samples() / vtl_dist); /* +2 left+right partial frames */
/* expand timeline (cache next/prev page images) */
vtl_start -= visible_video_frames * vtl_dist;
visible_video_frames *=3;
/* don't request frames that are too far to the right */
if (vtl_start < video_offset) {
visible_video_frames = std::max((double)0.0, (double)visible_video_frames + ceil((double)(vtl_start - video_offset)/vtl_dist));
vtl_start = video_offset;
}
/* apply video-file constraints
* (first frame in video is at video_start_offset) */
if (vtl_start > video_start_offset + video_duration + video_offset ) {
visible_video_frames = 0;
}
/* trim end.
* end = position on timeline (video-offset) minus video-file's first frame position
* TODO optimize: compute rather than iterate */
while (visible_video_frames > 0 && vtl_start + (visible_video_frames-1) * vtl_dist >= video_start_offset + video_duration + video_offset) {
--visible_video_frames;
}
if (_flush_frames) {
remove_frames ();
_flush_frames = false;
}
while (video_frames.size() < visible_video_frames) {
VideoImageFrame *frame;
frame = new VideoImageFrame(*editor, *videotl_group, display_vframe_width, bar_height, video_server_url, translated_filename());
frame->ImgChanged.connect (*this, invalidator (*this), boost::bind (&PublicEditor::queue_visual_videotimeline_update, editor), gui_context());
video_frames.push_back(frame);
}
VideoFrames outdated_video_frames;
std::list<int> remaining;
outdated_video_frames = video_frames;
#if 1
/* when zoomed out, ignore shifts by +-1 sample
* which can occur due to rounding errors when
* scrolling to a new leftmost-audio sample.
*/
int cut =1;
if (vtl_dist/apv < 3.0) cut =0;
#else
int cut =0;
#endif
for (unsigned int vfcount=0; vfcount < visible_video_frames; ++vfcount){
samplepos_t vfpos = vtl_start + vfcount * vtl_dist; /* unit: audio-samples */
samplepos_t vframeno = rint ( (vfpos - video_offset) / apv); /* unit: video-frames */
vfpos = (vframeno * apv ) + video_offset; /* audio-sample corresponding to /rounded/ video-frame */
int rightend = -1; /* unit: pixels */
if (vfpos + vtl_dist > video_start_offset + video_duration + video_offset) {
rightend = display_vframe_width * (video_start_offset + video_duration + video_offset - vfpos) / vtl_dist;
//printf("lf(e): %lu\n", vframeno); // XXX
}
VideoImageFrame* frame = get_video_frame(vframeno, cut, rightend);
if (frame) {
frame->set_position(vfpos);
outdated_video_frames.remove (frame);
} else {
remaining.push_back(vfcount);
}
}
for (VideoFrames::iterator i = outdated_video_frames.begin(); i != outdated_video_frames.end(); ++i ) {
VideoImageFrame* frame = (*i);
if (remaining.empty()) {
frame->set_position(-2 * vtl_dist + leftmost_sample); /* move off screen */
} else {
int vfcount = remaining.front();
remaining.pop_front();
samplepos_t vfpos = vtl_start + vfcount * vtl_dist; /* unit: audio-samples */
samplepos_t vframeno = rint ((vfpos - video_offset) / apv); /* unit: video-frames */
int rightend = -1; /* unit: pixels */
if (vfpos + vtl_dist > video_start_offset + video_duration + video_offset) {
rightend = display_vframe_width * (video_start_offset + video_duration + video_offset - vfpos) / vtl_dist;
//printf("lf(n): %lu\n", vframeno); // XXX
}
frame->set_position(vfpos);
frame->set_videoframe(vframeno, rightend);
}
}
}
std::string
VideoTimeLine::translated_filename ()
{
if (!local_file){
return video_filename;
} else {
return video_map_path(server_docroot, video_filename);
}
}
bool
VideoTimeLine::video_file_info (std::string filename, bool local)
{
local_file = local;
if (Glib::path_is_absolute(filename) || !local_file)
{
video_filename = filename;
} else {
video_filename = Glib::build_filename (_session->session_directory().video_path(), filename);
}
long long int _duration;
double _start_offset;
if (!video_query_info(
video_server_url, translated_filename(),
video_file_fps, _duration, _start_offset, video_aspect_ratio)) {
warning << _("Parsing video file info failed. Is the Video Server running? Is the file readable by the Video Server? Does the docroot match? Is it a video file?") << endmsg;
video_duration = 0;
GuiUpdate("set-xjadeo-sensitive-off");
GuiUpdate("video-unavailable");
return false;
}
video_duration = _duration * _session->nominal_sample_rate() / video_file_fps;
video_start_offset = _start_offset * _session->nominal_sample_rate();
if (auto_set_session_fps && video_file_fps != _session->timecode_frames_per_second()) {
switch ((int)floorf(video_file_fps*1000.0)) {
case 23976:
_session->config.set_timecode_format(timecode_23976);
break;
case 24000:
_session->config.set_timecode_format(timecode_24);
break;
case 24975:
case 24976:
_session->config.set_timecode_format(timecode_24976);
break;
case 25000:
_session->config.set_timecode_format(timecode_25);
break;
case 29970:
_session->config.set_timecode_format(timecode_2997drop);
break;
case 30000:
_session->config.set_timecode_format(timecode_30);
break;
case 59940:
_session->config.set_timecode_format(timecode_5994);
break;
case 60000:
_session->config.set_timecode_format(timecode_60);
break;
default:
warning << string_compose (
_("Failed to set session-framerate: '%1' does not have a corresponding option setting in %2."),
video_file_fps, PROGRAM_NAME ) << endmsg;
break;
}
_session->config.set_video_pullup(0); /* TODO only set if set_timecode_format() was successful ?!*/
}
if (floor(video_file_fps*100) != floor(_session->timecode_frames_per_second()*100)) {
warning << string_compose(
_("Video file's framerate is not equal to %1 session timecode's framerate: '%2' vs '%3'"),
PROGRAM_NAME, video_file_fps, _session->timecode_frames_per_second())
<< endmsg;
}
flush_local_cache ();
if (found_xjadeo() && local_file) {
GuiUpdate("set-xjadeo-sensitive-on");
if (vmonitor && vmonitor->is_started()) {
#if 1
/* xjadeo <= 0.6.4 has a bug where changing the video-file may segfauls
* if the geometry changes to a different line-size alignment
*/
reopen_vmonitor = true;
vmonitor->quit();
#else
vmonitor->set_fps(video_file_fps);
vmonitor->open(video_filename);
#endif
}
} else if (!local_file) {
#if 1 /* temp debug/devel message */
// TODO - call xjremote remotely.
printf("the given video file can not be accessed on localhost, video monitoring is not currently supported for this case\n");
GuiUpdate("set-xjadeo-sensitive-off");
#endif
}
VtlUpdate();
GuiUpdate("video-available");
return true;
}
bool
VideoTimeLine::check_server ()
{
bool ok = false;
char url[1024];
snprintf(url, sizeof(url), "%s%sstatus"
, video_server_url.c_str()
, (video_server_url.length()>0 && video_server_url.at(video_server_url.length()-1) == '/')?"":"/"
);
char* res = ArdourCurl::http_get (url, NULL, false);
if (res) {
if (strstr(res, "status: ok, online.")) { ok = true; }
free(res);
}
return ok;
}
bool
VideoTimeLine::check_server_docroot ()
{
bool ok = true;
char url[1024];
std::vector<std::vector<std::string> > lines;
if (video_server_url.find("/localhost:") == string::npos) {
return true;
}
snprintf(url, sizeof(url), "%s%src?format=csv"
, video_server_url.c_str()
, (video_server_url.length()>0 && video_server_url.at(video_server_url.length()-1) == '/')?"":"/"
);
char* res = ArdourCurl::http_get (url, NULL, false);
if (!res) {
return false;
}
ParseCSV(std::string(res), lines);
if ( lines.empty()
|| lines.at(0).empty()
|| lines.at(0).at(0) != video_get_docroot(Config)) {
warning << string_compose(
_("Video-server docroot mismatch. %1: '%2', video-server: '%3'. This usually means that the video server was not started by %1 and uses a different document-root."),
PROGRAM_NAME, video_get_docroot(Config), lines.at(0).at(0))
<< endmsg;
ok = false; // TODO allow to override
}
free(res);
return ok;
}
void
VideoTimeLine::gui_update(std::string const & t) {
/* this is to be called via GuiUpdate() only. */
ENSURE_GUI_THREAD (*this, &VideoTimeLine::queue_visual_videotimeline_update)
if (t == "videotimeline-update") {
editor->queue_visual_videotimeline_update();
} else if (t == "set-xjadeo-active-off") {
editor->toggle_xjadeo_proc(0);
} else if (t == "set-xjadeo-active-on") {
editor->toggle_xjadeo_proc(1);
} else if (t == "set-xjadeo-sensitive-on") {
editor->set_xjadeo_sensitive(true);
} else if (t == "set-xjadeo-sensitive-off") {
editor->toggle_xjadeo_proc(0);
//close_video_monitor();
editor->set_xjadeo_sensitive(false);
} else if (t == "xjadeo-window-ontop-on") {
editor->toggle_xjadeo_viewoption(1, 1);
} else if (t == "xjadeo-window-ontop-off") {
editor->toggle_xjadeo_viewoption(1, 0);
} else if (t == "xjadeo-window-osd-timecode-on") {
editor->toggle_xjadeo_viewoption(2, 1);
} else if (t == "xjadeo-window-osd-timecode-off") {
editor->toggle_xjadeo_viewoption(2, 0);
} else if (t == "xjadeo-window-osd-frame-on") {
editor->toggle_xjadeo_viewoption(3, 1);
} else if (t == "xjadeo-window-osd-frame-off") {
editor->toggle_xjadeo_viewoption(3, 0);
} else if (t == "xjadeo-window-osd-box-on") {
editor->toggle_xjadeo_viewoption(4, 1);
} else if (t == "xjadeo-window-osd-box-off") {
editor->toggle_xjadeo_viewoption(4, 0);
} else if (t == "xjadeo-window-fullscreen-on") {
editor->toggle_xjadeo_viewoption(5, 1);
} else if (t == "xjadeo-window-fullscreen-off") {
editor->toggle_xjadeo_viewoption(5, 0);
} else if (t == "xjadeo-window-letterbox-on") {
editor->toggle_xjadeo_viewoption(6, 1);
} else if (t == "xjadeo-window-letterbox-off") {
editor->toggle_xjadeo_viewoption(6, 0);
} else if (t == "video-available") {
editor->set_close_video_sensitive(true);
} else if (t == "video-unavailable") {
editor->set_close_video_sensitive(false);
}
}
void
VideoTimeLine::set_height (int height) {
if (_session && bar_height != height) {
_session->set_dirty ();
}
bar_height = height;
flush_local_cache();
}
void
VideoTimeLine::vmon_update () {
if (vmonitor && vmonitor->is_started()) {
vmonitor->set_offset(video_offset); // TODO proper re-init xjadeo w/o restart not just offset.
}
}
void
VideoTimeLine::flush_local_cache () {
_flush_frames = true;
vmon_update();
}
void
VideoTimeLine::flush_cache () {
flush_local_cache();
char url[1024];
snprintf(url, sizeof(url), "%s%sadmin/flush_cache"
, video_server_url.c_str()
, (video_server_url.length()>0 && video_server_url.at(video_server_url.length()-1) == '/')?"":"/"
);
char* res = ArdourCurl::http_get (url, NULL, false);
if (res) {
free (res);
}
if (vmonitor && vmonitor->is_started()) {
reopen_vmonitor=true;
vmonitor->quit();
}
video_file_info(video_filename, local_file);
}
/* config */
void
VideoTimeLine::parameter_changed (std::string const & p)
{
if (p == "video-server-url") {
set_video_server_url (video_get_server_url(Config));
} else if (p == "video-server-docroot") {
set_video_server_docroot (video_get_docroot(Config));
} else if (p == "video-advanced-setup") {
set_video_server_url (video_get_server_url(Config));
set_video_server_docroot (video_get_docroot(Config));
}
if (p == "use-video-file-fps" || p == "videotimeline-pullup" ) { /* session->config parameter */
VtlUpdate();
}
}
void
VideoTimeLine::set_video_server_url(std::string vsu) {
flush_local_cache ();
video_server_url = vsu;
VtlUpdate();
}
void
VideoTimeLine::set_video_server_docroot(std::string vsr) {
flush_local_cache ();
server_docroot = vsr;
VtlUpdate();
}
/* video-monitor for this timeline */
void
VideoTimeLine::xjadeo_readversion (std::string d, size_t /* s */) {
xjadeo_version += d;
}
void
VideoTimeLine::find_xjadeo () {
if (!ArdourVideoToolPaths::xjadeo_exe(_xjadeo_bin)) {
warning << _("Video-monitor 'xjadeo' was not found. Please install http://xjadeo.sf.net/ "
"(a custom path to xjadeo can be specified by setting the XJREMOTE environment variable. "
"It should point to an application compatible with xjadeo's remote-control interface 'xjremote').\n"
"\n"
"see also http://manual.ardour.org/video-timeline/setup/")
<< endmsg;
}
if (found_xjadeo ()) {
ARDOUR::SystemExec version_check(_xjadeo_bin, X_("--version"));
xjadeo_version = "";
version_check.ReadStdout.connect_same_thread (*this, boost::bind (&VideoTimeLine::xjadeo_readversion, this, _1 ,_2));
version_check.Terminated.connect_same_thread (*this, boost::bind (&VideoTimeLine::xjadeo_readversion, this, "\n" ,1));
if (version_check.start (ARDOUR::SystemExec::MergeWithStdin)) {
warning << _(
"Video-monitor 'xjadeo' cannot be launched."
) << endmsg;
_xjadeo_bin = X_("");
return;
}
#ifdef PLATFORM_WINDOWS
version_check.wait (); // 40ms timeout
#else
version_check.wait (WNOHANG);
#endif
int timeout = 300;
while (xjadeo_version.empty() && --timeout) {
Glib::usleep(10000);
}
bool v_ok = false;
size_t vo = xjadeo_version.find(" version ");
if (vo != string::npos) {
int v_major, v_minor, v_micro;
if(sscanf(xjadeo_version.substr(vo + 9, string::npos).c_str(),"%d.%d.%d",
&v_major, &v_minor, &v_micro) == 3)
{
if (v_major >= 1) v_ok = true;
else if (v_major == 0 && v_minor >= 8) v_ok = true;
else if (v_major == 0 && v_minor >= 7 && v_micro >= 7) v_ok = true;
}
}
if (!v_ok) {
_xjadeo_bin = X_("");
warning << _(
"Video-monitor 'xjadeo' is too old. "
"Please install xjadeo version 0.7.7 or later. http://xjadeo.sf.net/"
) << endmsg;
}
}
}
void
VideoTimeLine::harvid_readversion (std::string d, size_t /* s */) {
harvid_version += d;
}
void
VideoTimeLine::find_harvid () {
/* This is mainly for the benefit of the windows version:
* harvid >= 0.8.2 allows an empty docroot and ardour can
* pass the drive-letter along.
*
* It is a chicken/egg w.r.t. the video-server dialog
* but needed for default preferences and initial settings.
*/
std::string harvid_bin;
if (VideoUtils::harvid_version != 0x0) {
return;
}
if (!ArdourVideoToolPaths::harvid_exe(harvid_bin)) {
return;
}
if (harvid_bin.empty ()) {
return;
}
ARDOUR::SystemExec version_check(harvid_bin, X_("--version"));
harvid_version = "";
version_check.ReadStdout.connect_same_thread (*this, boost::bind (&VideoTimeLine::harvid_readversion, this, _1 ,_2));
version_check.Terminated.connect_same_thread (*this, boost::bind (&VideoTimeLine::harvid_readversion, this, "\n" ,1));
if (version_check.start (ARDOUR::SystemExec::MergeWithStdin)) {
return;
}
#ifdef PLATFORM_WINDOWS
version_check.wait (); // 40ms timeout
#else
version_check.wait (WNOHANG);
#endif
int timeout = 300;
while (harvid_version.empty() && --timeout) {
Glib::usleep(10000);
}
size_t vo = harvid_version.find("harvid v");
if (vo != string::npos) {
int v_major, v_minor, v_micro;
if(sscanf(harvid_version.substr(vo + 8, string::npos).c_str(),"%d.%d.%d",
&v_major, &v_minor, &v_micro) == 3)
{
VideoUtils::harvid_version = (v_major << 16) | (v_minor << 8) | v_micro;
info << "harvid version: "<< hex << VideoUtils::harvid_version << endmsg;
}
}
}
void
VideoTimeLine::open_video_monitor() {
if (!found_xjadeo()) return;
if (!vmonitor) {
vmonitor = new VideoMonitor(editor, _xjadeo_bin);
vmonitor->set_session(_session);
vmonitor->set_offset(video_offset);
vmonitor->Terminated.connect (sigc::mem_fun (*this, &VideoTimeLine::terminated_video_monitor));
vmonitor->UiState.connect (*this, invalidator (*this), boost::bind (&VideoTimeLine::gui_update, this, _1), gui_context());
} else if (vmonitor->is_started()) {
return;
}
#if 0
/* unused for now.
* the idea is to selective ignore certain monitor window
* states if xjadeo is not running on the same host as ardour.
* However with the removal of the video-monitor-startup-dialogue
* (git rev 5a4d0fff0) these settings are currently not accessible.
*/
int xj_settings_mask = vmonitor->restore_settings_mask();
if (_session) {
/* load mask from Session */
XMLNode* node = _session->extra_xml (X_("XJRestoreSettings"));
if (node) {
XMLProperty const * prop = node->property (X_("mask"));
if (prop) {
xj_settings_mask = atoi(prop->value());
}
}
}
vmonitor->restore_settings_mask(xj_settings_mask);
#endif
if (!vmonitor->start()) {
warning << "launching xjadeo failed.." << endmsg;
close_video_monitor();
} else {
GuiUpdate("set-xjadeo-active-on");
vmonitor->set_fps(video_file_fps);
vmonitor->open(video_filename);
if (_session) {
XMLNode* node = _session->extra_xml (X_("Videomonitor"));
if (node) {
bool active;
if (node->get_property (X_("active"), active) && !active) {
_session->set_dirty ();
}
} else {
_session->set_dirty ();
}
}
}
}
void
VideoTimeLine::close_video_monitor() {
if (vmonitor && vmonitor->is_started()) {
vmonitor->quit();
}
}
void
VideoTimeLine::control_video_monitor(int what, int param) {
if (!vmonitor || !vmonitor->is_started()) {
return;
}
vmonitor->send_cmd(what, param);
}
void
VideoTimeLine::terminated_video_monitor () {
if (vmonitor) {
vmonitor->save_session();
delete vmonitor;
}
vmonitor=0;
GuiUpdate("set-xjadeo-active-off");
if (reopen_vmonitor) {
reopen_vmonitor=false;
open_video_monitor();
} else {
if (_session) {
_session->set_dirty ();
}
}
}
void
VideoTimeLine::manual_seek_video_monitor (samplepos_t pos)
{
if (!vmonitor) { return; }
if (!vmonitor->is_started()) { return; }
if (!vmonitor->synced_by_manual_seeks()) { return; }
vmonitor->manual_seek(pos, false, video_offset); // XXX -> set offset in xjadeo
}