2007-01-21 07:30:19 +01:00
// Copyright (c) 2005-2007, Rodrigo Braz Monteiro
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
// * Neither the name of the Aegisub Group nor the names of its contributors
// may be used to endorse or promote products derived from this software
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
2009-07-29 07:43:02 +02:00
// Aegisub Project http://www.aegisub.org/
2007-01-21 07:30:19 +01:00
//
2009-07-29 07:43:02 +02:00
// $Id$
/// @file video_context.cpp
2010-12-07 20:09:28 +01:00
/// @brief Keep track of loaded video
2009-07-29 07:43:02 +02:00
/// @ingroup video
///
2007-01-21 07:30:19 +01:00
2009-01-04 07:31:48 +01:00
# include "config.h"
2009-09-10 15:06:40 +02:00
# ifndef AGI_PRE
# include <string.h>
# include <wx/clipbrd.h>
# include <wx/config.h>
# include <wx/filename.h>
# include <wx/image.h>
2011-01-16 08:18:06 +01:00
# include <wx/msgdlg.h>
2009-09-10 15:06:40 +02:00
# endif
2011-01-16 08:17:08 +01:00
# include <libaegisub/access.h>
# include <libaegisub/keyframe.h>
2007-01-21 07:30:19 +01:00
# include "ass_dialogue.h"
2009-09-10 15:06:40 +02:00
# include "ass_file.h"
2007-01-21 07:30:19 +01:00
# include "ass_style.h"
2009-09-10 15:06:40 +02:00
# include "ass_time.h"
2010-12-08 04:36:10 +01:00
# include "audio_controller.h"
2010-05-21 03:13:36 +02:00
# include "compat.h"
2010-08-02 08:31:38 +02:00
# include "include/aegisub/audio_player.h"
# include "include/aegisub/audio_provider.h"
2011-01-16 08:17:08 +01:00
# include "include/aegisub/context.h"
2010-07-23 07:58:39 +02:00
# include "include/aegisub/video_provider.h"
2010-05-21 03:13:36 +02:00
# include "main.h"
2010-05-19 02:44:37 +02:00
# include "mkv_wrap.h"
2010-12-08 04:36:10 +01:00
# include "selection_controller.h"
2011-01-16 08:17:08 +01:00
# include "standard_paths.h"
2010-07-23 07:58:39 +02:00
# include "threaded_frame_source.h"
2009-09-10 15:06:40 +02:00
# include "utils.h"
# include "video_context.h"
2010-08-02 08:32:01 +02:00
# include "video_frame.h"
2007-01-21 07:30:19 +01:00
2010-07-08 06:29:04 +02:00
/// IDs
2007-01-21 07:30:19 +01:00
enum {
VIDEO_PLAY_TIMER = 1300
} ;
BEGIN_EVENT_TABLE ( VideoContext , wxEvtHandler )
EVT_TIMER ( VIDEO_PLAY_TIMER , VideoContext : : OnPlayTimer )
END_EVENT_TABLE ( )
Note: This was done using a script! it's far from perfect but 95% of the work has been done already formatting-wise.
Document all functions, class, struct, union, enum, macro, variable, typedefs. This isn't the actual document in itself but empty documentation using any old documentation if it was there.
This was done using exuberant ctags to get tag info, then a TCL script to parse/remove old comments and convert them into Doxygen-style.
Some notes:
* Anything labeled 'DOCME' needs to be documented, @param and @return have been left blank as it would be annoying to delete the 'DOCME' from every one of those.
* Some multiline comments may have been munged into single line comments
* Leave the /// comments above global variables with a space, if they're harder to read then we'll be less likey to use them.
* Enum comments can go after the enumeration itself '[value] /// comment'
* include/aegisub/*.h haven't been converted yet, this will be done in a later commit
* Some documentation blocks are in the wrong place, in the .h when it should be in the .cpp, or vice versa.
See http://devel.aegisub.org/wiki/Doxygen for some details on Doxygen and a 'style guide'.
Originally committed to SVN as r3312.
2009-07-30 00:59:22 +02:00
/// @brief Constructor
///
2010-05-19 02:44:37 +02:00
VideoContext : : VideoContext ( )
2010-07-18 07:45:52 +02:00
: startFrame ( - 1 )
2010-05-20 10:55:58 +02:00
, endFrame ( - 1 )
, playNextFrame ( - 1 )
, nextFrame ( - 1 )
, isPlaying ( false )
, keepAudioSync ( true )
2010-05-19 02:44:37 +02:00
, frame_n ( 0 )
, length ( 0 )
, arValue ( 1. )
2010-05-20 10:55:58 +02:00
, arType ( 0 )
2010-05-19 02:44:37 +02:00
, hasSubtitles ( false )
2010-06-11 04:24:59 +02:00
, playAudioOnStep ( OPT_GET ( " Audio/Plays When Stepping Video " ) )
2010-07-08 06:29:04 +02:00
, VFR_Input ( videoFPS )
, VFR_Output ( ovrFPS )
2010-05-19 02:44:37 +02:00
{
2010-07-23 07:58:39 +02:00
Bind ( EVT_VIDEO_ERROR , & VideoContext : : OnVideoError , this ) ;
Bind ( EVT_SUBTITLES_ERROR , & VideoContext : : OnSubtitlesError , this ) ;
2010-08-26 20:38:37 +02:00
2010-12-07 20:09:15 +01:00
OPT_SUB ( " Subtitle/Provider " , & VideoContext : : Reload , this ) ;
OPT_SUB ( " Video/Provider " , & VideoContext : : Reload , this ) ;
2010-08-26 20:38:37 +02:00
// It would be nice to find a way to move these to the individual providers
2010-12-07 20:09:15 +01:00
OPT_SUB ( " Provider/Avisynth/Allow Ancient " , & VideoContext : : Reload , this ) ;
OPT_SUB ( " Provider/Avisynth/Memory Max " , & VideoContext : : Reload , this ) ;
2010-08-26 20:38:37 +02:00
2010-12-07 20:09:15 +01:00
OPT_SUB ( " Provider/Video/FFmpegSource/Decoding Threads " , & VideoContext : : Reload , this ) ;
OPT_SUB ( " Provider/Video/FFmpegSource/Unsafe Seeking " , & VideoContext : : Reload , this ) ;
2007-01-21 07:30:19 +01:00
}
VideoContext : : ~ VideoContext ( ) {
}
VideoContext * VideoContext : : Get ( ) {
2010-07-08 06:29:04 +02:00
static VideoContext instance ;
return & instance ;
2007-01-21 07:30:19 +01:00
}
void VideoContext : : Reset ( ) {
2011-09-28 21:43:11 +02:00
StandardPaths : : SetPathValue ( " ?video " , " " ) ;
2007-06-21 00:23:55 +02:00
2010-07-08 06:29:04 +02:00
keyFrames . clear ( ) ;
2010-12-31 22:03:18 +01:00
keyFramesFilename . clear ( ) ;
2010-07-08 06:29:04 +02:00
videoFPS = agi : : vfr : : Framerate ( ) ;
2010-12-31 22:03:18 +01:00
KeyframesOpen ( keyFrames ) ;
if ( ! ovrFPS . IsLoaded ( ) ) TimecodesOpen ( videoFPS ) ;
2007-01-21 07:30:19 +01:00
// Remove video data
frame_n = 0 ;
2009-05-01 00:13:10 +02:00
length = 0 ;
2007-01-21 07:30:19 +01:00
isPlaying = false ;
nextFrame = - 1 ;
// Clean up video data
2010-07-08 06:29:04 +02:00
videoName . clear ( ) ;
2007-01-21 07:30:19 +01:00
// Remove provider
2010-07-23 07:58:39 +02:00
videoProvider . reset ( ) ;
2010-07-08 06:29:04 +02:00
provider . reset ( ) ;
2007-04-08 21:27:46 +02:00
}
2011-01-16 08:17:08 +01:00
void VideoContext : : SetContext ( agi : : Context * context ) {
this - > context = context ;
context - > ass - > AddCommitListener ( & VideoContext : : OnSubtitlesCommit , this ) ;
2011-08-27 08:42:03 +02:00
context - > ass - > AddFileSaveListener ( & VideoContext : : OnSubtitlesSave , this ) ;
2011-01-16 08:17:08 +01:00
}
2007-01-21 07:30:19 +01:00
void VideoContext : : SetVideo ( const wxString & filename ) {
2010-07-08 06:29:04 +02:00
Stop ( ) ;
2007-01-21 07:30:19 +01:00
Reset ( ) ;
2011-01-16 08:16:27 +01:00
if ( filename . empty ( ) ) {
VideoOpen ( ) ;
return ;
}
2009-10-06 18:12:23 +02:00
2010-07-08 06:29:04 +02:00
try {
2010-08-27 03:01:35 +02:00
provider . reset ( new ThreadedFrameSource ( filename , this ) ) ;
2010-07-23 08:40:12 +02:00
videoProvider = provider - > GetVideoProvider ( ) ;
2010-08-26 20:38:37 +02:00
videoFile = filename ;
2010-07-08 06:29:04 +02:00
2010-07-23 07:58:39 +02:00
keyFrames = videoProvider - > GetKeyFrames ( ) ;
2010-07-08 06:29:04 +02:00
// Set frame rate
2010-07-23 07:58:39 +02:00
videoFPS = videoProvider - > GetFPS ( ) ;
2010-07-08 06:29:04 +02:00
if ( ovrFPS . IsLoaded ( ) ) {
int ovr = wxMessageBox ( _ ( " You already have timecodes loaded. Would you like to replace them with timecodes from the video file? " ) , _ ( " Replace timecodes? " ) , wxYES_NO | wxICON_QUESTION ) ;
if ( ovr = = wxYES ) {
ovrFPS = agi : : vfr : : Framerate ( ) ;
ovrTimecodeFile . clear ( ) ;
2009-06-24 20:16:03 +02:00
}
2010-07-08 06:29:04 +02:00
}
2007-01-21 07:30:19 +01:00
2010-07-08 06:29:04 +02:00
// Gather video parameters
2010-07-23 07:58:39 +02:00
length = videoProvider - > GetFrameCount ( ) ;
2007-01-21 07:30:19 +01:00
2010-07-08 06:29:04 +02:00
// Set filename
videoName = filename ;
config : : mru - > Add ( " Video " , STD_STR ( filename ) ) ;
wxFileName fn ( filename ) ;
2011-09-28 21:43:11 +02:00
StandardPaths : : SetPathValue ( " ?video " , fn . GetPath ( ) ) ;
2007-07-29 11:06:38 +02:00
2010-07-08 06:29:04 +02:00
// Get frame
frame_n = 0 ;
2010-02-14 23:02:33 +01:00
2010-07-08 06:29:04 +02:00
// Show warning
2010-07-23 07:58:39 +02:00
wxString warning = videoProvider - > GetWarning ( ) ;
2011-09-28 21:43:11 +02:00
if ( ! warning . empty ( ) ) wxMessageBox ( warning , " Warning " , wxICON_WARNING | wxOK ) ;
2010-05-19 02:44:37 +02:00
2010-07-08 06:29:04 +02:00
hasSubtitles = false ;
2011-09-28 21:43:11 +02:00
if ( filename . Right ( 4 ) . Lower ( ) = = " .mkv " ) {
2010-07-08 06:29:04 +02:00
hasSubtitles = MatroskaWrapper : : HasSubtitles ( filename ) ;
2007-01-21 07:30:19 +01:00
}
2010-07-08 06:29:04 +02:00
2011-01-16 08:17:08 +01:00
provider - > LoadSubtitles ( context - > ass ) ;
2010-12-07 20:09:21 +01:00
VideoOpen ( ) ;
KeyframesOpen ( keyFrames ) ;
2010-12-31 22:03:18 +01:00
TimecodesOpen ( FPS ( ) ) ;
2010-07-08 06:29:04 +02:00
}
2010-08-02 08:32:01 +02:00
catch ( agi : : UserCancelException const & ) { }
catch ( agi : : FileNotAccessibleError const & err ) {
config : : mru - > Remove ( " Video " , STD_STR ( filename ) ) ;
2011-09-28 21:43:11 +02:00
wxMessageBox ( lagi_wxString ( err . GetMessage ( ) ) , " Error setting video " , wxICON_ERROR | wxOK ) ;
2010-07-23 08:40:12 +02:00
}
2010-08-02 08:32:01 +02:00
catch ( VideoProviderError const & err ) {
2011-09-28 21:43:11 +02:00
wxMessageBox ( lagi_wxString ( err . GetMessage ( ) ) , " Error setting video " , wxICON_ERROR | wxOK ) ;
2007-01-21 07:30:19 +01:00
}
}
2010-08-26 20:38:37 +02:00
void VideoContext : : Reload ( ) {
if ( IsLoaded ( ) ) {
int frame = frame_n ;
SetVideo ( videoFile ) ;
JumpToFrame ( frame ) ;
}
}
2011-01-16 08:16:40 +01:00
void VideoContext : : OnSubtitlesCommit ( ) {
2010-07-23 07:58:39 +02:00
if ( ! IsLoaded ( ) ) return ;
2010-07-20 05:11:11 +02:00
2010-12-07 20:09:28 +01:00
bool wasPlaying = isPlaying ;
Stop ( ) ;
2011-01-16 08:17:08 +01:00
provider - > LoadSubtitles ( context - > ass ) ;
2010-12-07 20:09:28 +01:00
GetFrameAsync ( frame_n ) ;
if ( wasPlaying ) Play ( ) ;
2007-01-21 07:30:19 +01:00
}
2011-01-16 08:16:40 +01:00
void VideoContext : : OnSubtitlesSave ( ) {
if ( ! IsLoaded ( ) ) {
2011-01-16 08:17:08 +01:00
context - > ass - > SetScriptInfo ( " Video File " , " " ) ;
context - > ass - > SetScriptInfo ( " Video Aspect Ratio " , " " ) ;
context - > ass - > SetScriptInfo ( " Video Position " , " " ) ;
context - > ass - > SetScriptInfo ( " VFR File " , " " ) ;
context - > ass - > SetScriptInfo ( " Keyframes File " , " " ) ;
2011-01-16 08:16:40 +01:00
return ;
}
wxString ar ;
if ( arType = = 4 )
ar = wxString : : Format ( " c%g " , arValue ) ;
else
ar = wxString : : Format ( " %d " , arType ) ;
2011-01-16 08:17:08 +01:00
context - > ass - > SetScriptInfo ( " Video File " , MakeRelativePath ( videoName , context - > ass - > filename ) ) ;
context - > ass - > SetScriptInfo ( " Video Aspect Ratio " , ar ) ;
context - > ass - > SetScriptInfo ( " Video Position " , wxString : : Format ( " %d " , frame_n ) ) ;
context - > ass - > SetScriptInfo ( " VFR File " , MakeRelativePath ( GetTimecodesName ( ) , context - > ass - > filename ) ) ;
context - > ass - > SetScriptInfo ( " Keyframes File " , MakeRelativePath ( GetKeyFramesName ( ) , context - > ass - > filename ) ) ;
2011-01-16 08:16:40 +01:00
}
2007-01-21 07:30:19 +01:00
void VideoContext : : JumpToFrame ( int n ) {
2010-07-08 06:29:04 +02:00
if ( ! IsLoaded ( ) ) return ;
2007-01-21 07:30:19 +01:00
// Prevent intervention during playback
if ( isPlaying & & n ! = playNextFrame ) return ;
2011-01-21 05:57:36 +01:00
frame_n = mid ( 0 , n , GetLength ( ) - 1 ) ;
2007-04-08 01:45:46 +02:00
2011-11-06 18:18:20 +01:00
GetFrameAsync ( frame_n ) ;
Seek ( frame_n ) ;
2007-01-21 07:30:19 +01:00
}
2010-07-08 06:29:04 +02:00
void VideoContext : : JumpToTime ( int ms , agi : : vfr : : Time end ) {
JumpToFrame ( FrameAtTime ( ms , end ) ) ;
2007-01-21 07:30:19 +01:00
}
2010-07-23 07:58:39 +02:00
void VideoContext : : GetFrameAsync ( int n ) {
2011-11-06 18:18:20 +01:00
provider - > RequestFrame ( n , videoFPS . TimeAtFrame ( n ) / 1000.0 ) ;
2010-07-23 07:58:39 +02:00
}
2007-04-03 04:55:43 +02:00
2010-09-16 00:10:48 +02:00
std : : tr1 : : shared_ptr < AegiVideoFrame > VideoContext : : GetFrame ( int n , bool raw ) {
2011-11-06 18:18:20 +01:00
return provider - > GetFrame ( n , videoFPS . TimeAtFrame ( n ) / 1000.0 , raw ) ;
2007-04-03 04:55:43 +02:00
}
2010-07-08 06:29:04 +02:00
int VideoContext : : GetWidth ( ) const {
2010-07-23 07:58:39 +02:00
return videoProvider - > GetWidth ( ) ;
2010-07-08 06:29:04 +02:00
}
int VideoContext : : GetHeight ( ) const {
2010-07-23 07:58:39 +02:00
return videoProvider - > GetHeight ( ) ;
2010-07-08 06:29:04 +02:00
}
2007-04-08 00:03:06 +02:00
void VideoContext : : SaveSnapshot ( bool raw ) {
2007-01-21 07:30:19 +01:00
// Get folder
2010-11-08 06:08:24 +01:00
static const agi : : OptionValue * ssPath = OPT_GET ( " Path/Screenshot " ) ;
2010-06-08 08:09:13 +02:00
wxString option = lagi_wxString ( ssPath - > GetString ( ) ) ;
2007-01-21 07:30:19 +01:00
wxFileName videoFile ( videoName ) ;
wxString basepath ;
2008-01-10 23:28:47 +01:00
// Is it a path specifier and not an actual fixed path?
2011-09-28 21:43:11 +02:00
if ( option [ 0 ] = = ' ? ' ) {
2008-01-10 23:28:47 +01:00
// If dummy video is loaded, we can't save to the video location
2011-09-28 21:43:11 +02:00
if ( option . StartsWith ( " ?video " ) & & ( videoName . Find ( " ?dummy " ) ! = wxNOT_FOUND ) ) {
2008-01-10 23:28:47 +01:00
// So try the script location instead
2011-09-28 21:43:11 +02:00
option = " ?script " ;
2008-01-10 23:28:47 +01:00
}
// Find out where the ?specifier points to
2007-06-21 02:46:50 +02:00
basepath = StandardPaths : : DecodePath ( option ) ;
2010-07-08 06:29:04 +02:00
// If where ever that is isn't defined, we can't save there
2011-09-28 21:43:11 +02:00
if ( ( basepath = = " \\ " ) | | ( basepath = = " / " ) ) {
2008-01-10 23:28:47 +01:00
// So save to the current user's home dir instead
basepath = wxGetHomeDir ( ) ;
}
2007-01-21 07:30:19 +01:00
}
2008-01-10 23:28:47 +01:00
// Actual fixed (possibly relative) path, decode it
2011-09-28 21:43:11 +02:00
else basepath = DecodeRelativePath ( option , StandardPaths : : DecodePath ( " ?user/ " ) ) ;
basepath + = " / " + videoFile . GetName ( ) ;
2007-01-21 07:30:19 +01:00
// Get full path
int session_shot_count = 1 ;
wxString path ;
while ( 1 ) {
2011-09-28 21:43:11 +02:00
path = basepath + wxString : : Format ( " _%03i_%i.png " , session_shot_count , frame_n ) ;
2007-01-21 07:30:19 +01:00
+ + session_shot_count ;
wxFileName tryPath ( path ) ;
if ( ! tryPath . FileExists ( ) ) break ;
}
2010-09-16 00:10:48 +02:00
GetFrame ( frame_n , raw ) - > GetImage ( ) . SaveFile ( path , wxBITMAP_TYPE_PNG ) ;
2007-01-21 07:30:19 +01:00
}
2011-01-16 08:15:53 +01:00
void VideoContext : : NextFrame ( ) {
if ( ! videoProvider . get ( ) | | isPlaying | | frame_n = = videoProvider - > GetFrameCount ( ) )
2010-02-17 20:04:41 +01:00
return ;
JumpToFrame ( frame_n + 1 ) ;
// Start playing audio
2010-12-08 04:36:10 +01:00
if ( playAudioOnStep - > GetBool ( ) ) {
2011-01-16 08:17:08 +01:00
context - > audioController - > PlayRange ( SampleRange (
context - > audioController - > SamplesFromMilliseconds ( TimeAtFrame ( frame_n - 1 ) ) ,
context - > audioController - > SamplesFromMilliseconds ( TimeAtFrame ( frame_n ) ) ) ) ;
2010-12-08 04:36:10 +01:00
}
2010-02-17 20:04:41 +01:00
}
2011-01-16 08:15:53 +01:00
void VideoContext : : PrevFrame ( ) {
if ( ! videoProvider . get ( ) | | isPlaying | | frame_n = = 0 )
2010-02-17 20:04:41 +01:00
return ;
2011-01-16 08:15:53 +01:00
JumpToFrame ( frame_n - 1 ) ;
2010-02-17 20:04:41 +01:00
// Start playing audio
2010-12-08 04:36:10 +01:00
if ( playAudioOnStep - > GetBool ( ) ) {
2011-01-16 08:17:08 +01:00
context - > audioController - > PlayRange ( SampleRange (
context - > audioController - > SamplesFromMilliseconds ( TimeAtFrame ( frame_n ) ) ,
context - > audioController - > SamplesFromMilliseconds ( TimeAtFrame ( frame_n + 1 ) ) ) ) ;
2010-12-08 04:36:10 +01:00
}
2010-02-17 20:04:41 +01:00
}
2007-01-21 07:30:19 +01:00
void VideoContext : : Play ( ) {
if ( isPlaying ) {
Stop ( ) ;
return ;
}
2011-10-29 06:30:52 +02:00
if ( ! IsLoaded ( ) ) return ;
2007-01-21 07:30:19 +01:00
// Set variables
startFrame = frame_n ;
endFrame = - 1 ;
// Start playing audio
2011-01-16 08:17:08 +01:00
context - > audioController - > PlayToEnd ( context - > audioController - > SamplesFromMilliseconds ( TimeAtFrame ( startFrame ) ) ) ;
2007-01-21 07:30:19 +01:00
2007-03-18 02:20:25 +01:00
//audio->Play will override this if we put it before, so put it after.
isPlaying = true ;
2007-01-21 07:30:19 +01:00
// Start timer
2008-01-20 07:14:40 +01:00
playTime . Start ( ) ;
2007-01-21 07:30:19 +01:00
playback . SetOwner ( this , VIDEO_PLAY_TIMER ) ;
2008-01-20 07:14:40 +01:00
playback . Start ( 10 ) ;
2007-01-21 07:30:19 +01:00
}
void VideoContext : : PlayLine ( ) {
2011-01-16 08:17:08 +01:00
AssDialogue * curline = context - > selectionController - > GetActiveLine ( ) ;
2007-01-21 07:30:19 +01:00
if ( ! curline ) return ;
// Start playing audio
2011-01-16 08:17:08 +01:00
context - > audioController - > PlayRange ( SampleRange (
context - > audioController - > SamplesFromMilliseconds ( curline - > Start . GetMS ( ) ) ,
context - > audioController - > SamplesFromMilliseconds ( curline - > End . GetMS ( ) ) ) ) ;
2007-01-21 07:30:19 +01:00
// Set variables
isPlaying = true ;
2011-01-16 08:17:08 +01:00
startFrame = FrameAtTime ( context - > selectionController - > GetActiveLine ( ) - > Start . GetMS ( ) , agi : : vfr : : START ) ;
endFrame = FrameAtTime ( context - > selectionController - > GetActiveLine ( ) - > End . GetMS ( ) , agi : : vfr : : END ) ;
2007-01-21 07:30:19 +01:00
// Jump to start
playNextFrame = startFrame ;
JumpToFrame ( startFrame ) ;
// Set other variables
2008-01-20 20:51:48 +01:00
playTime . Start ( ) ;
2007-01-21 07:30:19 +01:00
// Start timer
playback . SetOwner ( this , VIDEO_PLAY_TIMER ) ;
2008-01-20 07:14:40 +01:00
playback . Start ( 10 ) ;
2007-01-21 07:30:19 +01:00
}
void VideoContext : : Stop ( ) {
if ( isPlaying ) {
playback . Stop ( ) ;
isPlaying = false ;
2011-01-16 08:17:08 +01:00
context - > audioController - > Stop ( ) ;
2007-01-21 07:30:19 +01:00
}
}
void VideoContext : : OnPlayTimer ( wxTimerEvent & event ) {
// Lock
wxMutexError res = playMutex . TryLock ( ) ;
if ( res = = wxMUTEX_BUSY ) return ;
playMutex . Unlock ( ) ;
wxMutexLocker lock ( playMutex ) ;
// Get time difference
2008-01-20 07:14:40 +01:00
int dif = playTime . Time ( ) ;
2007-01-21 07:30:19 +01:00
// Find next frame
2010-07-08 06:29:04 +02:00
int startMs = TimeAtFrame ( startFrame ) ;
2007-01-21 07:30:19 +01:00
int nextFrame = frame_n ;
int i = 0 ;
for ( i = 0 ; i < 10 ; i + + ) {
if ( nextFrame > = length ) break ;
2010-07-08 06:29:04 +02:00
if ( dif < TimeAtFrame ( nextFrame ) - startMs ) {
2007-01-21 07:30:19 +01:00
break ;
}
nextFrame + + ;
}
// End
if ( nextFrame > = length | | ( endFrame ! = - 1 & & nextFrame > endFrame ) ) {
Stop ( ) ;
return ;
}
// Same frame
if ( nextFrame = = frame_n ) return ;
// Next frame is before or over 2 frames ahead, so force audio resync
2011-01-16 08:17:08 +01:00
if ( context - > audioController - > IsPlaying ( ) & & keepAudioSync & & ( nextFrame < frame_n | | nextFrame > frame_n + 2 ) ) {
context - > audioController - > ResyncPlaybackPosition ( context - > audioController - > SamplesFromMilliseconds ( TimeAtFrame ( nextFrame ) ) ) ;
2010-12-08 04:36:10 +01:00
}
2007-01-21 07:30:19 +01:00
// Jump to next frame
playNextFrame = nextFrame ;
frame_n = nextFrame ;
JumpToFrame ( nextFrame ) ;
// Sync audio
2011-01-16 08:17:08 +01:00
if ( keepAudioSync & & nextFrame % 10 = = 0 & & context - > audioController - > IsPlaying ( ) ) {
int64_t audPos = context - > audioController - > SamplesFromMilliseconds ( TimeAtFrame ( nextFrame ) ) ;
int64_t curPos = context - > audioController - > GetPlaybackPosition ( ) ;
2007-01-21 07:30:19 +01:00
int delta = int ( audPos - curPos ) ;
if ( delta < 0 ) delta = - delta ;
2011-01-16 08:17:08 +01:00
int maxDelta = context - > audioController - > SamplesFromMilliseconds ( 1000 ) ;
if ( delta > maxDelta ) context - > audioController - > ResyncPlaybackPosition ( audPos ) ;
2007-01-21 07:30:19 +01:00
}
}
2010-07-08 06:29:04 +02:00
double VideoContext : : GetARFromType ( int type ) const {
2011-01-16 08:17:08 +01:00
if ( type = = 0 ) return ( double ) GetWidth ( ) / ( double ) GetHeight ( ) ;
2010-07-08 06:29:04 +02:00
if ( type = = 1 ) return 4.0 / 3.0 ;
if ( type = = 2 ) return 16.0 / 9.0 ;
if ( type = = 3 ) return 2.35 ;
return 1.0 ; //error
2007-01-21 07:30:19 +01:00
}
2010-07-08 06:29:04 +02:00
void VideoContext : : SetAspectRatio ( int type , double value ) {
if ( type ! = 4 ) value = GetARFromType ( type ) ;
2007-01-21 07:30:19 +01:00
2010-07-08 06:29:04 +02:00
arType = type ;
2010-12-31 22:03:03 +01:00
arValue = mid ( .5 , value , 5. ) ;
2010-12-07 20:09:28 +01:00
ARChange ( arType , arValue ) ;
2007-01-21 07:30:19 +01:00
}
2010-07-08 06:29:04 +02:00
void VideoContext : : LoadKeyframes ( wxString filename ) {
if ( filename = = keyFramesFilename | | filename . empty ( ) ) return ;
2010-08-02 10:18:53 +02:00
try {
2011-11-20 18:35:00 +01:00
keyFrames = agi : : keyframe : : Load ( STD_STR ( filename ) ) ;
2010-08-02 10:18:53 +02:00
keyFramesFilename = filename ;
2010-12-07 20:09:21 +01:00
KeyframesOpen ( keyFrames ) ;
2010-12-31 22:02:17 +01:00
config : : mru - > Add ( " Keyframes " , STD_STR ( filename ) ) ;
2010-08-02 10:18:53 +02:00
}
2010-12-31 22:02:17 +01:00
catch ( agi : : keyframe : : Error const & err ) {
wxMessageBox ( err . GetMessage ( ) , " Error opening keyframes file " , wxOK | wxICON_ERROR , NULL ) ;
config : : mru - > Remove ( " Keyframes " , STD_STR ( filename ) ) ;
2010-08-02 10:18:53 +02:00
}
2010-12-31 22:02:17 +01:00
catch ( agi : : acs : : AcsError const & ) {
2011-09-28 21:43:11 +02:00
wxLogError ( " Could not open file " + filename ) ;
2010-08-02 10:18:53 +02:00
config : : mru - > Remove ( " Keyframes " , STD_STR ( filename ) ) ;
}
2007-01-21 07:30:19 +01:00
}
2010-07-08 06:29:04 +02:00
void VideoContext : : SaveKeyframes ( wxString filename ) {
2011-11-20 18:35:00 +01:00
agi : : keyframe : : Save ( STD_STR ( filename ) , GetKeyFrames ( ) ) ;
2010-12-31 22:02:17 +01:00
config : : mru - > Add ( " Keyframes " , STD_STR ( filename ) ) ;
2007-01-21 07:30:19 +01:00
}
2010-07-08 06:29:04 +02:00
void VideoContext : : CloseKeyframes ( ) {
keyFramesFilename . clear ( ) ;
2011-11-20 18:35:00 +01:00
if ( videoProvider )
2010-07-23 07:58:39 +02:00
keyFrames = videoProvider - > GetKeyFrames ( ) ;
2011-11-20 18:35:00 +01:00
else
2010-07-08 06:29:04 +02:00
keyFrames . clear ( ) ;
2010-12-07 20:09:21 +01:00
KeyframesOpen ( keyFrames ) ;
2007-01-21 07:30:19 +01:00
}
2010-07-08 06:29:04 +02:00
void VideoContext : : LoadTimecodes ( wxString filename ) {
if ( filename = = ovrTimecodeFile | | filename . empty ( ) ) return ;
try {
ovrFPS = agi : : vfr : : Framerate ( STD_STR ( filename ) ) ;
ovrTimecodeFile = filename ;
config : : mru - > Add ( " Timecodes " , STD_STR ( filename ) ) ;
2011-01-16 08:16:40 +01:00
OnSubtitlesCommit ( ) ;
2010-12-31 22:03:18 +01:00
TimecodesOpen ( ovrFPS ) ;
2010-07-08 06:29:04 +02:00
}
catch ( const agi : : acs : : AcsError & ) {
2011-09-28 21:43:11 +02:00
wxLogError ( " Could not open file " + filename ) ;
2010-08-02 10:18:53 +02:00
config : : mru - > Remove ( " Timecodes " , STD_STR ( filename ) ) ;
2010-07-08 06:29:04 +02:00
}
catch ( const agi : : vfr : : Error & e ) {
2011-09-28 21:43:48 +02:00
wxLogError ( " Timecode file parse error: %s " , e . GetMessage ( ) ) ;
2010-07-08 06:29:04 +02:00
}
2007-01-21 07:30:19 +01:00
}
2010-07-08 06:29:04 +02:00
void VideoContext : : SaveTimecodes ( wxString filename ) {
try {
2010-10-16 22:11:37 +02:00
FPS ( ) . Save ( STD_STR ( filename ) , IsLoaded ( ) ? length : - 1 ) ;
2010-07-08 06:29:04 +02:00
config : : mru - > Add ( " Timecodes " , STD_STR ( filename ) ) ;
}
catch ( const agi : : acs : : AcsError & ) {
2011-09-28 21:43:11 +02:00
wxLogError ( " Could not write to " + filename ) ;
2010-07-08 06:29:04 +02:00
}
}
void VideoContext : : CloseTimecodes ( ) {
ovrFPS = agi : : vfr : : Framerate ( ) ;
ovrTimecodeFile . clear ( ) ;
2011-01-16 08:16:40 +01:00
OnSubtitlesCommit ( ) ;
2010-12-31 22:03:18 +01:00
TimecodesOpen ( videoFPS ) ;
2007-01-23 07:32:16 +01:00
}
2010-07-08 06:29:04 +02:00
int VideoContext : : TimeAtFrame ( int frame , agi : : vfr : : Time type ) const {
if ( ovrFPS . IsLoaded ( ) ) {
return ovrFPS . TimeAtFrame ( frame , type ) ;
}
return videoFPS . TimeAtFrame ( frame , type ) ;
}
int VideoContext : : FrameAtTime ( int time , agi : : vfr : : Time type ) const {
if ( ovrFPS . IsLoaded ( ) ) {
return ovrFPS . FrameAtTime ( time , type ) ;
}
return videoFPS . FrameAtTime ( time , type ) ;
2007-01-23 07:32:16 +01:00
}
2010-07-23 07:58:39 +02:00
void VideoContext : : OnVideoError ( VideoProviderErrorEvent const & err ) {
wxLogError (
2011-09-28 21:43:11 +02:00
" Failed seeking video. The video file may be corrupt or incomplete. \n "
" Error message reported: %s " ,
2011-09-28 21:43:48 +02:00
lagi_wxString ( err . GetMessage ( ) ) ) ;
2010-07-23 07:58:39 +02:00
}
void VideoContext : : OnSubtitlesError ( SubtitlesProviderErrorEvent const & err ) {
wxLogError (
2011-09-28 21:43:11 +02:00
" Failed rendering subtitles. Error message reported: %s " ,
2011-09-28 21:43:48 +02:00
lagi_wxString ( err . GetMessage ( ) ) ) ;
2010-07-23 07:58:39 +02:00
}
2010-08-27 03:01:35 +02:00
void VideoContext : : OnExit ( ) {
// On unix wxThreadModule will shut down any still-running threads (and
// display a warning that it's doing so) before the destructor for
// VideoContext runs, so manually kill the thread
Get ( ) - > provider . reset ( ) ;
}