2013-03-12 17:00:09 -04:00
/*
2019-08-02 17:26:43 -04:00
* 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 .
*/
2013-03-27 13:45:08 -04:00
# include <algorithm>
2013-03-12 17:00:09 -04:00
# include <sigc++/bind.h>
# include "ardour/tempo.h"
# include "pbd/file_utils.h"
2016-08-29 09:34:13 -04:00
# include "pbd/types_convert.h"
2018-11-18 21:20:11 -05:00
# include "ardour/filesystem_paths.h"
2013-03-12 17:00:09 -04:00
# include "ardour/session_directory.h"
# include "ardour_ui.h"
2016-07-18 10:10:35 -04:00
# include "ardour_http.h"
2013-03-12 17:00:09 -04:00
# 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>
2016-07-14 14:44:52 -04:00
# include "pbd/i18n.h"
2013-03-12 17:00:09 -04:00
using namespace std ;
using namespace ARDOUR ;
using namespace PBD ;
using namespace Timecode ;
2013-08-03 10:54:57 -04:00
using namespace VideoUtils ;
2013-03-12 17:00:09 -04:00
2014-06-22 11:41:05 -04:00
VideoTimeLine : : VideoTimeLine ( PublicEditor * ed , ArdourCanvas : : Container * vbg , int initial_height )
2013-03-12 17:00:09 -04:00
: editor ( ed )
2018-01-21 05:27:26 -05:00
, videotl_group ( vbg )
, bar_height ( initial_height )
2013-03-12 17:00:09 -04:00
{
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 ;
2018-01-21 05:27:26 -05:00
_flush_frames = false ;
2013-03-12 17:00:09 -04:00
vmonitor = 0 ;
reopen_vmonitor = false ;
find_xjadeo ( ) ;
2017-01-10 16:38:43 -05:00
find_harvid ( ) ;
2017-01-10 21:55:04 -05:00
video_server_url = video_get_server_url ( Config ) ;
server_docroot = video_get_docroot ( Config ) ;
2013-03-12 17:00:09 -04:00
2024-10-18 19:51:44 -04:00
VtlUpdate . connect ( * this , invalidator ( * this ) , std : : bind ( & PublicEditor : : queue_visual_videotimeline_update , editor ) , gui_context ( ) ) ;
GuiUpdate . connect ( * this , invalidator ( * this ) , std : : bind ( & VideoTimeLine : : gui_update , this , _1 ) , gui_context ( ) ) ;
2013-03-12 17:00:09 -04:00
}
VideoTimeLine : : ~ VideoTimeLine ( )
{
close_session ( ) ;
}
/* close and save settings */
void
VideoTimeLine : : save_session ( )
{
if ( ! _session ) {
return ;
}
XMLNode * node = new XMLNode ( X_ ( " Videomonitor " ) ) ;
2013-04-08 22:05:07 -04:00
if ( ! node ) return ;
2016-08-29 09:34:13 -04:00
node - > set_property ( X_ ( " active " ) , ( vmonitor & & vmonitor - > is_started ( ) ) ) ;
2013-04-08 22:05:07 -04:00
_session - > add_extra_xml ( * node ) ;
if ( vmonitor ) {
2013-04-09 11:13:42 -04:00
if ( vmonitor - > is_started ( ) ) {
vmonitor - > query_full_state ( true ) ;
}
2013-04-08 22:05:07 -04:00
vmonitor - > save_session ( ) ;
2013-03-12 17:00:09 -04:00
}
/* VTL settings */
node = _session - > extra_xml ( X_ ( " Videotimeline " ) ) ;
2013-04-08 22:05:07 -04:00
if ( ! node ) return ;
2016-08-29 09:34:13 -04:00
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 ) ;
2013-03-12 17:00:09 -04:00
}
/* close and save settings */
void
VideoTimeLine : : close_session ( )
{
2013-03-29 04:46:29 -04:00
if ( video_duration = = 0 ) {
return ;
}
2013-04-08 21:47:47 -04:00
sessionsave . disconnect ( ) ;
2013-04-03 16:29:04 -04:00
close_video_monitor ( ) ;
2013-03-12 17:00:09 -04:00
2018-01-21 05:27:26 -05:00
remove_frames ( ) ;
2013-03-12 17:00:09 -04:00
video_filename = " " ;
2013-03-29 04:46:29 -04:00
video_duration = 0 ;
GuiUpdate ( " set-xjadeo-sensitive-off " ) ;
2013-06-13 09:35:41 -04:00
GuiUpdate ( " video-unavailable " ) ;
2013-03-12 17:00:09 -04:00
}
2013-04-08 22:05:07 -04:00
void
VideoTimeLine : : sync_session_state ( )
{
if ( ! _session | | ! vmonitor | | ! vmonitor - > is_started ( ) ) {
return ;
}
save_session ( ) ;
}
2013-03-12 17:00:09 -04:00
/** load settings from session */
void
VideoTimeLine : : set_session ( ARDOUR : : Session * s )
{
SessionHandlePtr : : set_session ( s ) ;
if ( ! _session ) { return ; }
2024-10-18 19:51:44 -04:00
_session - > SessionSaveUnderway . connect_same_thread ( sessionsave , std : : bind ( & VideoTimeLine : : save_session , this ) ) ;
2013-03-12 17:00:09 -04:00
XMLNode * node = _session - > extra_xml ( X_ ( " Videotimeline " ) ) ;
2013-03-29 04:46:29 -04:00
if ( ! node | | ! node - > property ( X_ ( " Filename " ) ) ) {
return ;
}
2013-06-07 09:35:49 -04:00
ARDOUR_UI : : instance ( ) - > start_video_server ( ( Gtk : : Window * ) 0 , false ) ;
2013-03-12 17:00:09 -04:00
2013-06-07 09:35:49 -04:00
set_id ( * node ) ;
2013-03-12 17:00:09 -04:00
2016-08-29 09:34:13 -04:00
int height ;
if ( node - > get_property ( X_ ( " Height " ) , height ) ) {
editor - > set_video_timeline_height ( height ) ;
2013-06-07 09:35:49 -04:00
}
2013-03-12 17:00:09 -04:00
#if 0 /* TODO THINK: set FPS first time only ?! */
2016-05-04 23:09:37 -04:00
XMLProperty const * propasfps = node - > property ( X_ ( " AutoFPS " ) ) ;
2013-06-07 09:35:49 -04:00
if ( propasfps ) {
2013-06-08 03:03:26 -04:00
auto_set_session_fps = atoi ( propasfps - > value ( ) ) ? true : false ;
2013-06-07 09:35:49 -04:00
}
2013-03-12 17:00:09 -04:00
# endif
2016-08-29 09:34:13 -04:00
if ( node - > get_property ( X_ ( " VideoOffset " ) , video_offset ) ) {
2013-06-07 09:35:49 -04:00
video_offset_p = video_offset ;
}
2013-03-12 17:00:09 -04:00
2016-08-29 09:34:13 -04:00
node - > get_property ( X_ ( " VideoOffsetLock " ) , video_offset_lock ) ;
node - > get_property ( X_ ( " LocalFile " ) , local_file ) ;
2013-03-12 17:00:09 -04:00
2016-08-29 09:34:13 -04:00
std : : string filename ;
if ( node - > get_property ( X_ ( " Filename " ) , filename ) ) {
video_file_info ( filename , local_file ) ;
2013-03-12 17:00:09 -04:00
}
2013-06-07 09:35:49 -04:00
if ( ( node = _session - > extra_xml ( X_ ( " Videomonitor " ) ) ) ) {
2016-08-29 09:34:13 -04:00
bool active ;
if ( node - > get_property ( X_ ( " active " ) , active ) & & active & & found_xjadeo ( ) & &
! video_filename . empty ( ) & & local_file ) {
2013-04-02 11:57:09 -04:00
open_video_monitor ( ) ;
2013-03-12 17:00:09 -04:00
}
}
_session - > register_with_memento_command_factory ( id ( ) , this ) ;
_session - > config . ParameterChanged . connect ( * this , invalidator ( * this ) , ui_bind ( & VideoTimeLine : : parameter_changed , this , _1 ) , gui_context ( ) ) ;
}
2013-04-08 15:17:48 -04:00
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 ( ) ;
}
}
2013-03-12 17:00:09 -04:00
void
VideoTimeLine : : save_undo ( )
{
2013-04-08 15:11:17 -04:00
if ( _session & & video_offset_p ! = video_offset ) {
_session - > set_dirty ( ) ;
}
2013-03-12 17:00:09 -04:00
video_offset_p = video_offset ;
}
int
VideoTimeLine : : set_state ( const XMLNode & node , int /*version*/ )
{
2016-08-29 09:34:13 -04:00
node . get_property ( X_ ( " VideoOffset " ) , video_offset ) ;
2013-03-12 17:00:09 -04:00
ARDOUR_UI : : instance ( ) - > flush_videotimeline_cache ( true ) ;
return 0 ;
}
XMLNode &
2022-04-06 23:56:45 -04:00
VideoTimeLine : : get_state ( ) const
2013-03-12 17:00:09 -04:00
{
XMLNode * node = new XMLNode ( X_ ( " Videotimeline " ) ) ;
2016-08-29 09:34:13 -04:00
node - > set_property ( X_ ( " VideoOffset " ) , video_offset_p ) ;
2013-03-12 17:00:09 -04:00
return * node ;
}
void
2018-01-21 05:27:26 -05:00
VideoTimeLine : : remove_frames ( )
2013-03-12 17:00:09 -04:00
{
2018-01-21 05:27:26 -05:00
for ( VideoFrames : : iterator i = video_frames . begin ( ) ; i ! = video_frames . end ( ) ; + + i ) {
VideoImageFrame * frame = ( * i ) ;
delete frame ;
2013-03-12 17:00:09 -04:00
( * i ) = 0 ;
}
video_frames . clear ( ) ;
}
2018-01-21 05:27:26 -05:00
VideoImageFrame *
2017-09-18 12:39:17 -04:00
VideoTimeLine : : get_video_frame ( samplepos_t vfn , int cut , int rightend )
2013-03-12 17:00:09 -04:00
{
if ( vfn = = 0 ) cut = 0 ;
2018-01-21 05:27:26 -05:00
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 ; }
2013-03-12 17:00:09 -04:00
}
return 0 ;
}
float
VideoTimeLine : : get_apv ( )
{
// XXX: dup code - TODO use this fn in update_video_timeline()
2013-04-17 03:09:56 -04:00
float apv = - 1 ; /* audio samples per video frame; */
2013-03-12 17:00:09 -04:00
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 ( ) ) {
2017-09-18 12:39:17 -04:00
apv = _session - > sample_rate ( ) ;
2013-03-12 17:00:09 -04:00
} else {
2017-09-18 12:39:17 -04:00
apv = _session - > nominal_sample_rate ( ) ;
2013-03-12 17:00:09 -04:00
}
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 ;
}
2013-04-17 03:09:56 -04:00
const double samples_per_pixel = editor - > get_current_zoom ( ) ;
2017-09-18 12:39:17 -04:00
const samplepos_t leftmost_sample = editor - > leftmost_sample ( ) ;
2013-03-12 17:00:09 -04:00
/* Outline:
2018-01-21 05:27:26 -05:00
* 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 .
2013-03-12 17:00:09 -04:00
*/
/* video-file and session properties */
double display_vframe_width ; /* unit: pixels ; width of one thumbnail in the timeline */
2013-04-17 03:09:56 -04:00
float apv ; /* audio samples per video frame; */
2017-09-18 12:39:17 -04:00
samplepos_t leftmost_video_frame ; /* unit: video-frame number ; temporary var -> vtl_start */
2013-03-12 17:00:09 -04:00
/* variables needed to render videotimeline -- what needs to computed first */
2017-09-18 12:39:17 -04:00
samplepos_t vtl_start ; /* unit: audio-samples ; first displayed video-frame */
samplepos_t vtl_dist ; /* unit: audio-samples ; distance between displayed video-frames */
2018-01-21 05:27:26 -05:00
unsigned int visible_video_frames ; /* number of frames that fit on current canvas */
2013-03-12 17:00:09 -04:00
if ( _session - > config . get_videotimeline_pullup ( ) ) {
2017-09-18 12:39:17 -04:00
apv = _session - > sample_rate ( ) ;
2013-03-12 17:00:09 -04:00
} else {
2017-09-18 12:39:17 -04:00
apv = _session - > nominal_sample_rate ( ) ;
2013-03-12 17:00:09 -04:00
}
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 ;
2013-04-17 03:09:56 -04:00
if ( apv > samples_per_pixel * display_vframe_width ) {
2013-03-12 17:00:09 -04:00
/* high-zoom: need space between successive video-frames */
vtl_dist = rint ( apv ) ;
} else {
Fix typos in gtk2_ardour/ directory
Found via `codespell -q 3 -S *.po,./share/patchfiles,./libs -L ba,buss,busses,doubleclick,hsi,ontop,ro,seh,siz,sur,te,trough,ue`
2022-01-26 12:35:38 -05:00
/* continuous timeline: skip video-frames */
2013-04-17 03:09:56 -04:00
vtl_dist = ceil ( display_vframe_width * samples_per_pixel / apv ) * apv ;
2013-03-12 17:00:09 -04:00
}
assert ( vtl_dist > 0 ) ;
assert ( apv > 0 ) ;
2013-10-03 04:06:56 -04:00
leftmost_video_frame = floor ( floor ( ( long double ) ( leftmost_sample - video_start_offset - video_offset ) / vtl_dist ) * vtl_dist / apv ) ;
2013-03-12 17:00:09 -04:00
2013-06-10 13:33:54 -04:00
vtl_start = rint ( video_offset + video_start_offset + leftmost_video_frame * apv ) ;
2018-01-21 05:27:26 -05:00
visible_video_frames = 2 + ceil ( ( double ) editor - > current_page_samples ( ) / vtl_dist ) ; /* +2 left+right partial frames */
2013-03-12 17:00:09 -04:00
/* expand timeline (cache next/prev page images) */
vtl_start - = visible_video_frames * vtl_dist ;
visible_video_frames * = 3 ;
2018-01-21 05:27:26 -05:00
/* don't request frames that are too far to the right */
2015-06-09 17:26:43 -04:00
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 ) ) ;
2013-06-10 13:33:54 -04:00
vtl_start = video_offset ;
2013-03-12 17:00:09 -04:00
}
2015-06-09 17:26:43 -04:00
/* apply video-file constraints
2018-01-21 05:27:26 -05:00
* ( first frame in video is at video_start_offset ) */
2013-06-10 13:33:54 -04:00
if ( vtl_start > video_start_offset + video_duration + video_offset ) {
2013-03-12 17:00:09 -04:00
visible_video_frames = 0 ;
}
2015-06-09 17:26:43 -04:00
/* trim end.
2018-01-21 05:27:26 -05:00
* end = position on timeline ( video - offset ) minus video - file ' s first frame position
2015-06-09 17:26:43 -04:00
* TODO optimize : compute rather than iterate */
2013-06-10 13:33:54 -04:00
while ( visible_video_frames > 0 & & vtl_start + ( visible_video_frames - 1 ) * vtl_dist > = video_start_offset + video_duration + video_offset ) {
2013-03-12 17:00:09 -04:00
- - visible_video_frames ;
}
2018-01-21 05:27:26 -05:00
if ( _flush_frames ) {
remove_frames ( ) ;
_flush_frames = false ;
2013-03-12 17:00:09 -04:00
}
while ( video_frames . size ( ) < visible_video_frames ) {
2018-01-21 05:27:26 -05:00
VideoImageFrame * frame ;
frame = new VideoImageFrame ( * editor , * videotl_group , display_vframe_width , bar_height , video_server_url , translated_filename ( ) ) ;
2024-10-18 19:51:44 -04:00
frame - > ImgChanged . connect ( * this , invalidator ( * this ) , std : : bind ( & PublicEditor : : queue_visual_videotimeline_update , editor ) , gui_context ( ) ) ;
2018-01-21 05:27:26 -05:00
video_frames . push_back ( frame ) ;
2013-03-12 17:00:09 -04:00
}
2018-01-21 05:27:26 -05:00
VideoFrames outdated_video_frames ;
2013-03-12 17:00:09 -04:00
std : : list < int > remaining ;
outdated_video_frames = video_frames ;
# if 1
2017-09-18 12:39:17 -04:00
/* when zoomed out, ignore shifts by +-1 sample
2013-03-12 17:00:09 -04:00
* which can occur due to rounding errors when
2017-09-18 12:39:17 -04:00
* scrolling to a new leftmost - audio sample .
2013-03-12 17:00:09 -04:00
*/
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 ) {
2017-09-18 12:39:17 -04:00
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 */
2013-03-12 17:00:09 -04:00
int rightend = - 1 ; /* unit: pixels */
2013-06-10 13:33:54 -04:00
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 ;
2013-03-12 17:00:09 -04:00
//printf("lf(e): %lu\n", vframeno); // XXX
}
2018-01-21 05:27:26 -05:00
VideoImageFrame * frame = get_video_frame ( vframeno , cut , rightend ) ;
if ( frame ) {
frame - > set_position ( vfpos ) ;
outdated_video_frames . remove ( frame ) ;
2013-03-12 17:00:09 -04:00
} else {
remaining . push_back ( vfcount ) ;
}
}
2018-01-21 05:27:26 -05:00
for ( VideoFrames : : iterator i = outdated_video_frames . begin ( ) ; i ! = outdated_video_frames . end ( ) ; + + i ) {
VideoImageFrame * frame = ( * i ) ;
2013-03-12 17:00:09 -04:00
if ( remaining . empty ( ) ) {
2018-01-21 05:27:26 -05:00
frame - > set_position ( - 2 * vtl_dist + leftmost_sample ) ; /* move off screen */
2013-03-12 17:00:09 -04:00
} else {
2018-01-21 05:27:26 -05:00
int vfcount = remaining . front ( ) ;
2013-03-12 17:00:09 -04:00
remaining . pop_front ( ) ;
2017-09-18 12:39:17 -04:00
samplepos_t vfpos = vtl_start + vfcount * vtl_dist ; /* unit: audio-samples */
samplepos_t vframeno = rint ( ( vfpos - video_offset ) / apv ) ; /* unit: video-frames */
2013-03-12 17:00:09 -04:00
int rightend = - 1 ; /* unit: pixels */
2013-06-10 13:33:54 -04:00
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 ;
2013-03-12 17:00:09 -04:00
//printf("lf(n): %lu\n", vframeno); // XXX
}
2018-01-21 05:27:26 -05:00
frame - > set_position ( vfpos ) ;
frame - > set_videoframe ( vframeno , rightend ) ;
2013-03-12 17:00:09 -04:00
}
}
}
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 )
{
2013-03-27 13:21:09 -04:00
2013-03-12 17:00:09 -04:00
local_file = local ;
2014-04-18 20:03:43 -04:00
if ( Glib : : path_is_absolute ( filename ) | | ! local_file )
{
2013-03-12 17:00:09 -04:00
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 ;
2013-06-13 09:35:41 -04:00
video_duration = 0 ;
GuiUpdate ( " set-xjadeo-sensitive-off " ) ;
2013-06-12 18:54:43 -04:00
GuiUpdate ( " video-unavailable " ) ;
2013-03-12 17:00:09 -04:00
return false ;
}
2017-09-18 12:39:17 -04:00
video_duration = _duration * _session - > nominal_sample_rate ( ) / video_file_fps ;
video_start_offset = _start_offset * _session - > nominal_sample_rate ( ) ;
2013-03-12 17:00:09 -04:00
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 :
2013-07-25 08:19:28 -04:00
warning < < string_compose (
_ ( " Failed to set session-framerate: '%1' does not have a corresponding option setting in %2. " ) ,
video_file_fps , PROGRAM_NAME ) < < endmsg ;
2013-03-12 17:00:09 -04:00
break ;
}
_session - > config . set_video_pullup ( 0 ) ; /* TODO only set if set_timecode_format() was successful ?!*/
}
2013-06-07 08:37:15 -04:00
if ( floor ( video_file_fps * 100 ) ! = floor ( _session - > timecode_frames_per_second ( ) * 100 ) ) {
2013-07-25 08:19:28 -04:00
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 ;
2013-03-12 17:00:09 -04:00
}
flush_local_cache ( ) ;
if ( found_xjadeo ( ) & & local_file ) {
GuiUpdate ( " set-xjadeo-sensitive-on " ) ;
if ( vmonitor & & vmonitor - > is_started ( ) ) {
2013-03-29 07:35:21 -04:00
# 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
2013-03-12 17:00:09 -04:00
vmonitor - > set_fps ( video_file_fps ) ;
vmonitor - > open ( video_filename ) ;
2013-03-29 07:35:21 -04:00
# endif
2013-03-12 17:00:09 -04:00
}
} else if ( ! local_file ) {
2013-03-14 21:09:04 -04:00
# 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 " ) ;
2013-03-12 17:00:09 -04:00
GuiUpdate ( " set-xjadeo-sensitive-off " ) ;
2013-03-14 21:09:04 -04:00
# endif
2013-03-12 17:00:09 -04:00
}
VtlUpdate ( ) ;
2013-06-12 18:54:43 -04:00
GuiUpdate ( " video-available " ) ;
2013-03-12 17:00:09 -04:00
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 ) = = ' / ' ) ? " " : " / "
) ;
2018-06-02 13:24:17 -04:00
char * res = ArdourCurl : : http_get ( url , NULL , false ) ;
2013-03-12 17:00:09 -04:00
if ( res ) {
if ( strstr ( res , " status: ok, online. " ) ) { ok = true ; }
free ( res ) ;
}
return ok ;
}
2013-06-12 18:55:04 -04:00
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 ) = = ' / ' ) ? " " : " / "
) ;
2018-06-02 13:24:17 -04:00
char * res = ArdourCurl : : http_get ( url , NULL , false ) ;
2013-06-12 18:55:04 -04:00
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 (
2014-02-10 10:53:01 -05:00
_ ( " 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. " ) ,
2013-07-25 08:19:28 -04:00
PROGRAM_NAME , video_get_docroot ( Config ) , lines . at ( 0 ) . at ( 0 ) )
2013-06-12 18:55:04 -04:00
< < endmsg ;
ok = false ; // TODO allow to override
}
free ( res ) ;
return ok ;
}
2013-03-12 17:00:09 -04:00
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 ) ;
2013-04-03 16:29:04 -04:00
} 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 ) ;
2018-01-21 05:27:26 -05:00
} else if ( t = = " xjadeo-window-osd-frame-on " ) {
2013-04-03 16:29:04 -04:00
editor - > toggle_xjadeo_viewoption ( 3 , 1 ) ;
2018-01-21 05:27:26 -05:00
} else if ( t = = " xjadeo-window-osd-frame-off " ) {
2013-04-03 16:29:04 -04:00
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 ) ;
2013-04-03 18:51:03 -04:00
} 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 ) ;
2013-06-12 18:54:43 -04:00
} else if ( t = = " video-available " ) {
editor - > set_close_video_sensitive ( true ) ;
} else if ( t = = " video-unavailable " ) {
editor - > set_close_video_sensitive ( false ) ;
2013-03-12 17:00:09 -04:00
}
}
void
VideoTimeLine : : set_height ( int height ) {
2013-04-08 15:17:48 -04:00
if ( _session & & bar_height ! = height ) {
_session - > set_dirty ( ) ;
}
2013-03-12 17:00:09 -04:00
bar_height = height ;
flush_local_cache ( ) ;
}
void
VideoTimeLine : : vmon_update ( ) {
if ( vmonitor & & vmonitor - > is_started ( ) ) {
2013-06-10 13:33:54 -04:00
vmonitor - > set_offset ( video_offset ) ; // TODO proper re-init xjadeo w/o restart not just offset.
2013-03-12 17:00:09 -04:00
}
}
void
VideoTimeLine : : flush_local_cache ( ) {
2018-01-21 05:27:26 -05:00
_flush_frames = true ;
2013-03-12 17:00:09 -04:00
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 ) = = ' / ' ) ? " " : " / "
) ;
2018-06-02 13:24:17 -04:00
char * res = ArdourCurl : : http_get ( url , NULL , false ) ;
2013-03-12 17:00:09 -04:00
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 " ) {
2013-03-28 08:41:36 -04:00
set_video_server_url ( video_get_server_url ( Config ) ) ;
2013-03-12 17:00:09 -04:00
} else if ( p = = " video-server-docroot " ) {
2013-03-28 08:41:36 -04:00
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 ) ) ;
2013-03-12 17:00:09 -04:00
}
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 */
2014-06-06 16:35:05 -04:00
void
VideoTimeLine : : xjadeo_readversion ( std : : string d , size_t /* s */ ) {
xjadeo_version + = d ;
}
2013-03-12 17:00:09 -04:00
void
VideoTimeLine : : find_xjadeo ( ) {
2014-12-23 12:28:45 -05:00
if ( ! ArdourVideoToolPaths : : xjadeo_exe ( _xjadeo_bin ) ) {
2013-03-14 21:09:04 -04:00
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. "
2014-04-11 11:01:26 -04:00
" 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/ " )
2013-03-14 21:09:04 -04:00
< < endmsg ;
2013-03-12 17:00:09 -04:00
}
2014-12-23 12:28:45 -05:00
2014-06-06 16:35:05 -04:00
if ( found_xjadeo ( ) ) {
2022-04-01 12:15:35 -04:00
ARDOUR : : SystemExec version_check ( _xjadeo_bin , X_ ( " --version " ) , true ) ;
2014-06-06 16:35:05 -04:00
xjadeo_version = " " ;
2024-10-18 19:51:44 -04:00
version_check . ReadStdout . connect_same_thread ( * this , std : : bind ( & VideoTimeLine : : xjadeo_readversion , this , _1 , _2 ) ) ;
version_check . Terminated . connect_same_thread ( * this , std : : bind ( & VideoTimeLine : : xjadeo_readversion , this , " \n " , 1 ) ) ;
2019-03-05 13:06:29 -05:00
if ( version_check . start ( ARDOUR : : SystemExec : : MergeWithStdin ) ) {
2014-06-06 16:35:05 -04:00
warning < < _ (
" Video-monitor 'xjadeo' cannot be launched. "
) < < endmsg ;
_xjadeo_bin = X_ ( " " ) ;
return ;
}
2016-02-28 11:06:28 -05:00
# ifdef PLATFORM_WINDOWS
version_check . wait ( ) ; // 40ms timeout
# else
version_check . wait ( WNOHANG ) ;
# endif
2014-06-06 16:35:05 -04:00
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 ;
2015-10-23 17:34:21 -04:00
else if ( v_major = = 0 & & v_minor > = 7 & & v_micro > = 7 ) v_ok = true ;
2014-06-06 16:35:05 -04:00
}
2023-05-13 11:08:47 -04:00
if ( v_ok ) {
info < < string_compose ( " xjadeo version: %1.%2.%3 " , v_major , v_minor , v_micro ) < < endmsg ;
}
2014-06-06 16:35:05 -04:00
}
if ( ! v_ok ) {
_xjadeo_bin = X_ ( " " ) ;
warning < < _ (
" Video-monitor 'xjadeo' is too old. "
2015-10-23 17:34:21 -04:00
" Please install xjadeo version 0.7.7 or later. http://xjadeo.sf.net/ "
2014-06-06 16:35:05 -04:00
) < < endmsg ;
}
}
2013-03-12 17:00:09 -04:00
}
2017-01-10 16:38:43 -05:00
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 ;
}
2022-04-01 12:15:35 -04:00
ARDOUR : : SystemExec version_check ( harvid_bin , X_ ( " --version " ) , true ) ;
2017-01-10 16:38:43 -05:00
harvid_version = " " ;
2024-10-18 19:51:44 -04:00
version_check . ReadStdout . connect_same_thread ( * this , std : : bind ( & VideoTimeLine : : harvid_readversion , this , _1 , _2 ) ) ;
version_check . Terminated . connect_same_thread ( * this , std : : bind ( & VideoTimeLine : : harvid_readversion , this , " \n " , 1 ) ) ;
2019-03-05 13:06:29 -05:00
if ( version_check . start ( ARDOUR : : SystemExec : : MergeWithStdin ) ) {
2017-01-10 16:38:43 -05:00
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 ;
}
}
}
2013-03-12 17:00:09 -04:00
void
2013-04-02 11:57:09 -04:00
VideoTimeLine : : open_video_monitor ( ) {
2013-03-12 17:00:09 -04:00
if ( ! found_xjadeo ( ) ) return ;
if ( ! vmonitor ) {
vmonitor = new VideoMonitor ( editor , _xjadeo_bin ) ;
vmonitor - > set_session ( _session ) ;
2013-06-10 13:43:23 -04:00
vmonitor - > set_offset ( video_offset ) ;
2013-03-12 17:00:09 -04:00
vmonitor - > Terminated . connect ( sigc : : mem_fun ( * this , & VideoTimeLine : : terminated_video_monitor ) ) ;
2024-10-18 19:51:44 -04:00
vmonitor - > UiState . connect ( * this , invalidator ( * this ) , std : : bind ( & VideoTimeLine : : gui_update , this , _1 ) , gui_context ( ) ) ;
2013-03-12 17:00:09 -04:00
} else if ( vmonitor - > is_started ( ) ) {
return ;
}
2013-06-10 11:56:53 -04:00
#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 5 a4d0fff0 ) these settings are currently not accessible .
*/
2013-03-12 17:00:09 -04:00
int xj_settings_mask = vmonitor - > restore_settings_mask ( ) ;
if ( _session ) {
/* load mask from Session */
XMLNode * node = _session - > extra_xml ( X_ ( " XJRestoreSettings " ) ) ;
if ( node ) {
2016-05-04 23:09:37 -04:00
XMLProperty const * prop = node - > property ( X_ ( " mask " ) ) ;
2013-03-12 17:00:09 -04:00
if ( prop ) {
2013-06-08 03:03:26 -04:00
xj_settings_mask = atoi ( prop - > value ( ) ) ;
2013-03-12 17:00:09 -04:00
}
}
}
2013-04-02 11:57:09 -04:00
vmonitor - > restore_settings_mask ( xj_settings_mask ) ;
2013-06-10 11:56:53 -04:00
# endif
2013-03-12 17:00:09 -04:00
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 ) ;
2013-04-08 22:05:07 -04:00
if ( _session ) {
XMLNode * node = _session - > extra_xml ( X_ ( " Videomonitor " ) ) ;
if ( node ) {
2016-08-29 09:34:13 -04:00
bool active ;
if ( node - > get_property ( X_ ( " active " ) , active ) & & ! active ) {
_session - > set_dirty ( ) ;
}
2013-04-08 22:05:07 -04:00
} else {
_session - > set_dirty ( ) ;
}
}
2013-03-12 17:00:09 -04:00
}
}
void
VideoTimeLine : : close_video_monitor ( ) {
if ( vmonitor & & vmonitor - > is_started ( ) ) {
vmonitor - > quit ( ) ;
}
}
2013-04-03 16:29:04 -04:00
void
VideoTimeLine : : control_video_monitor ( int what , int param ) {
if ( ! vmonitor | | ! vmonitor - > is_started ( ) ) {
return ;
}
vmonitor - > send_cmd ( what , param ) ;
}
2013-03-12 17:00:09 -04:00
void
VideoTimeLine : : terminated_video_monitor ( ) {
if ( vmonitor ) {
2013-06-09 13:39:25 -04:00
vmonitor - > save_session ( ) ;
2013-03-12 17:00:09 -04:00
delete vmonitor ;
}
vmonitor = 0 ;
2013-07-31 14:19:57 -04:00
GuiUpdate ( " set-xjadeo-active-off " ) ;
2013-04-08 22:05:07 -04:00
if ( reopen_vmonitor ) {
2013-03-12 17:00:09 -04:00
reopen_vmonitor = false ;
2013-04-02 11:57:09 -04:00
open_video_monitor ( ) ;
2013-04-08 22:05:07 -04:00
} else {
if ( _session ) {
_session - > set_dirty ( ) ;
}
2013-03-12 17:00:09 -04:00
}
}
void
2017-09-18 12:39:17 -04:00
VideoTimeLine : : manual_seek_video_monitor ( samplepos_t pos )
2013-03-12 17:00:09 -04:00
{
if ( ! vmonitor ) { return ; }
if ( ! vmonitor - > is_started ( ) ) { return ; }
if ( ! vmonitor - > synced_by_manual_seeks ( ) ) { return ; }
2013-06-10 13:33:54 -04:00
vmonitor - > manual_seek ( pos , false , video_offset ) ; // XXX -> set offset in xjadeo
2013-03-12 17:00:09 -04:00
}