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.
//
// -----------------------------------------------------------------------------
//
// AEGISUB
//
// Website: http://aegisub.cellosoft.com
// Contact: mailto:zeratul@cellosoft.com
//
////////////
// Includes
2007-09-12 01:22:26 +02:00
# ifdef __APPLE__
2007-04-22 04:24:27 +02:00
# include <OpenGL/GL.h>
# include <OpenGL/glu.h>
# else
2007-01-22 23:57:45 +01:00
# include <GL/gl.h>
# include <GL/glu.h>
2007-04-22 04:24:27 +02:00
# endif
2007-01-21 07:30:19 +01:00
# include <wx/image.h>
# include <string.h>
# include <wx/clipbrd.h>
# include <wx/filename.h>
# include <wx/config.h>
# include "utils.h"
# include "video_display.h"
# include "video_context.h"
# include "video_provider.h"
2007-07-01 09:09:37 +02:00
# include "visual_tool.h"
2007-01-21 07:30:19 +01:00
# include "subtitles_provider.h"
# include "vfr.h"
# include "ass_file.h"
# include "ass_exporter.h"
# include "ass_time.h"
# include "ass_dialogue.h"
# include "ass_style.h"
# include "subs_grid.h"
# include "vfw_wrap.h"
2008-01-15 01:54:54 +01:00
# include "config.h"
2008-01-05 01:02:06 +01:00
# if !defined(__WINDOWS__) && !defined(__APPLE__)
2008-01-15 01:54:54 +01:00
# ifdef WITH_FFMPEG
2007-10-29 19:48:02 +01:00
# include "lavc_keyframes.h"
# endif
2008-01-15 01:54:54 +01:00
# endif
2007-01-21 07:30:19 +01:00
# include "mkv_wrap.h"
# include "options.h"
# include "subs_edit_box.h"
# include "audio_display.h"
# include "video_slider.h"
# include "video_box.h"
# include "utils.h"
2007-01-27 07:15:25 +01:00
# include "gl_wrap.h"
2007-06-21 00:23:55 +02:00
# include "standard_paths.h"
2007-01-21 07:30:19 +01:00
///////
// IDs
enum {
VIDEO_PLAY_TIMER = 1300
} ;
///////////////
// Event table
BEGIN_EVENT_TABLE ( VideoContext , wxEvtHandler )
EVT_TIMER ( VIDEO_PLAY_TIMER , VideoContext : : OnPlayTimer )
END_EVENT_TABLE ( )
////////////
// Instance
VideoContext * VideoContext : : instance = NULL ;
///////////////
// Constructor
VideoContext : : VideoContext ( ) {
// Set GL context
glContext = NULL ;
lastTex = 0 ;
lastFrame = - 1 ;
2007-01-27 07:15:25 +01:00
yv12shader = 0 ;
2007-01-21 07:30:19 +01:00
// Set options
audio = NULL ;
provider = NULL ;
subsProvider = NULL ;
curLine = NULL ;
loaded = false ;
keyFramesLoaded = false ;
overKeyFramesLoaded = false ;
frame_n = 0 ;
2007-01-23 07:32:16 +01:00
arType = 0 ;
arValue = 1.0 ;
2007-01-21 07:30:19 +01:00
isPlaying = false ;
nextFrame = - 1 ;
2007-06-02 15:48:36 +02:00
keepAudioSync = true ;
2007-04-08 01:45:46 +02:00
// Threads
2007-06-28 22:29:56 +02:00
//threaded = Options.AsBool(_T("Threaded Video"));
threaded = false ;
2007-04-08 01:45:46 +02:00
threadLocked = false ;
threadNextFrame = - 1 ;
2007-01-21 07:30:19 +01:00
}
//////////////
// Destructor
VideoContext : : ~ VideoContext ( ) {
Reset ( ) ;
delete glContext ;
glContext = NULL ;
}
////////////////
// Get Instance
VideoContext * VideoContext : : Get ( ) {
2007-01-23 21:50:41 +01:00
if ( ! instance ) {
instance = new VideoContext ;
}
2007-01-21 07:30:19 +01:00
return instance ;
}
/////////
// Clear
void VideoContext : : Clear ( ) {
instance - > audio = NULL ;
delete instance ;
instance = NULL ;
}
/////////
// Reset
void VideoContext : : Reset ( ) {
2007-06-21 00:23:55 +02:00
// Reset ?video path
StandardPaths : : SetPathValue ( _T ( " ?video " ) , _T ( " " ) ) ;
2007-01-27 07:15:25 +01:00
// Reset shader
if ( yv12shader ) {
OpenGLWrapper : : DestroyShaderProgram ( yv12shader ) ;
yv12shader = 0 ;
}
2007-01-21 07:30:19 +01:00
// Clear keyframes
KeyFrames . Clear ( ) ;
keyFramesLoaded = false ;
// Remove temporary audio provider
if ( audio & & audio - > temporary ) {
delete audio - > provider ;
audio - > provider = NULL ;
delete audio - > player ;
audio - > player = NULL ;
audio - > temporary = false ;
}
// Remove video data
loaded = false ;
frame_n = 0 ;
keyFramesLoaded = false ;
overKeyFramesLoaded = false ;
isPlaying = false ;
nextFrame = - 1 ;
2007-06-28 22:29:56 +02:00
curLine = NULL ;
2007-01-21 07:30:19 +01:00
// Update displays
UpdateDisplays ( true ) ;
// Remove textures
UnloadTexture ( ) ;
// Clean up video data
wxRemoveFile ( tempfile ) ;
tempfile = _T ( " " ) ;
videoName = _T ( " " ) ;
tempFrame . Clear ( ) ;
// Remove provider
2007-04-08 21:27:46 +02:00
if ( provider ) {
if ( subsProvider & & ! subsProvider - > LockedToVideo ( ) ) delete subsProvider ;
delete provider ;
provider = NULL ;
}
else delete subsProvider ;
2007-01-21 07:30:19 +01:00
subsProvider = NULL ;
2007-04-08 21:27:46 +02:00
}
////////////////
// Reload video
void VideoContext : : Reload ( ) {
if ( IsLoaded ( ) ) {
wxString name = videoName ;
int n = frame_n ;
SetVideo ( _T ( " " ) ) ;
SetVideo ( name ) ;
JumpToFrame ( n ) ;
}
2007-01-21 07:30:19 +01:00
}
//////////////////
// Unload texture
void VideoContext : : UnloadTexture ( ) {
// Remove textures
if ( lastTex ! = 0 ) {
glDeleteTextures ( 1 , & lastTex ) ;
lastTex = 0 ;
}
lastFrame = - 1 ;
}
///////////////////////
// Sets video filename
void VideoContext : : SetVideo ( const wxString & filename ) {
// Unload video
Reset ( ) ;
2007-04-10 04:55:23 +02:00
threaded = Options . AsBool ( _T ( " Threaded Video " ) ) ;
2007-01-21 07:30:19 +01:00
// Load video
if ( ! filename . IsEmpty ( ) ) {
try {
grid - > CommitChanges ( true ) ;
bool isVfr = false ;
double overFps = 0 ;
FrameRate temp ;
// Unload timecodes
//int unload = wxYES;
//if (VFR_Output.IsLoaded()) unload = wxMessageBox(_("Do you want to unload timecodes, too?"),_("Unload timecodes?"),wxYES_NO | wxICON_QUESTION);
//if (unload == wxYES) VFR_Output.Unload();
// Read extra data from file
bool mkvOpen = MatroskaWrapper : : wrapper . IsOpen ( ) ;
wxString ext = filename . Right ( 4 ) . Lower ( ) ;
KeyFrames . Clear ( ) ;
if ( ext = = _T ( " .mkv " ) | | mkvOpen ) {
// Parse mkv
if ( ! mkvOpen ) MatroskaWrapper : : wrapper . Open ( filename ) ;
// Get keyframes
KeyFrames = MatroskaWrapper : : wrapper . GetKeyFrames ( ) ;
keyFramesLoaded = true ;
// Ask to override timecodes
int override = wxYES ;
if ( VFR_Output . IsLoaded ( ) ) override = wxMessageBox ( _ ( " You already have timecodes loaded. Replace them with the timecodes from the Matroska file? " ) , _ ( " Replace timecodes? " ) , wxYES_NO | wxICON_QUESTION ) ;
if ( override = = wxYES ) {
MatroskaWrapper : : wrapper . SetToTimecodes ( temp ) ;
isVfr = temp . GetFrameRateType ( ) = = VFR ;
if ( isVfr ) {
overFps = temp . GetCommonFPS ( ) ;
MatroskaWrapper : : wrapper . SetToTimecodes ( VFR_Input ) ;
MatroskaWrapper : : wrapper . SetToTimecodes ( VFR_Output ) ;
}
}
// Close mkv
MatroskaWrapper : : wrapper . Close ( ) ;
}
2007-10-29 19:48:02 +01:00
2007-01-21 07:30:19 +01:00
else if ( ext = = _T ( " .avi " ) ) {
2008-01-15 01:54:54 +01:00
keyFramesLoaded = false ;
KeyFrames . Clear ( ) ;
2007-10-29 19:48:02 +01:00
# ifdef __WINDOWS__
2007-01-21 07:30:19 +01:00
KeyFrames = VFWWrapper : : GetKeyFrames ( filename ) ;
2008-01-15 01:54:54 +01:00
keyFramesLoaded = true ;
2007-10-29 19:48:02 +01:00
# else
2008-01-05 01:02:06 +01:00
# ifndef __APPLE__
2008-01-15 01:54:54 +01:00
# ifdef WITH_FFMPEG
2007-10-29 19:48:02 +01:00
LAVCKeyFrames k ( filename ) ;
KeyFrames = k . GetKeyFrames ( ) ;
2008-01-15 01:54:54 +01:00
keyFramesLoaded = true ;
# endif
2008-01-05 01:02:06 +01:00
# endif
2007-10-29 19:48:02 +01:00
# endif
2007-01-21 07:30:19 +01:00
}
2008-01-13 21:25:47 +01:00
// Check if the file is all keyframes
bool isAllKeyFrames = true ;
for ( unsigned int i = 1 ; i < KeyFrames . GetCount ( ) ; i + + ) {
// Is the last keyframe not this keyframe -1?
if ( KeyFrames [ i - 1 ] ! = ( i - 1 ) ) {
// It's not all keyframes, go ahead
isAllKeyFrames = false ;
break ;
}
}
2007-10-29 19:48:02 +01:00
2008-01-13 21:25:47 +01:00
// If it is all keyframes, discard the keyframe info as it is useless
if ( isAllKeyFrames ) {
KeyFrames . Clear ( ) ;
keyFramesLoaded = false ;
}
2007-01-21 07:30:19 +01:00
2007-01-27 08:13:29 +01:00
// Set GL context
2007-09-12 01:22:26 +02:00
# ifdef __WXMAC__
GetGLContext ( displayList . front ( ) ) - > SetCurrent ( ) ;
# else
2007-01-27 08:13:29 +01:00
GetGLContext ( displayList . front ( ) ) - > SetCurrent ( * displayList . front ( ) ) ;
2007-09-12 01:22:26 +02:00
# endif
2007-01-27 08:13:29 +01:00
2007-01-21 07:30:19 +01:00
// Choose a provider
provider = VideoProviderFactory : : GetProvider ( filename , overFps ) ;
2007-01-23 01:38:59 +01:00
loaded = provider ! = NULL ;
2007-01-21 07:30:19 +01:00
// Get subtitles provider
2007-01-23 01:38:59 +01:00
try {
subsProvider = provider - > GetAsSubtitlesProvider ( ) ;
if ( ! subsProvider ) subsProvider = SubtitlesProviderFactory : : GetProvider ( ) ;
}
catch ( wxString err ) { wxMessageBox ( _T ( " Error while loading subtitles provider: " ) + err , _T ( " Subtitles provider " ) ) ; }
catch ( const wchar_t * err ) { wxMessageBox ( _T ( " Error while loading subtitles provider: " ) + wxString ( err ) , _T ( " Subtitles provider " ) ) ; }
2007-01-21 07:30:19 +01:00
// Set frame rate
fps = provider - > GetFPS ( ) ;
2007-07-29 11:06:38 +02:00
if ( ! isVfr | | provider - > IsNativelyByFrames ( ) ) {
2007-01-21 07:30:19 +01:00
VFR_Input . SetCFR ( fps ) ;
if ( VFR_Output . GetFrameRateType ( ) ! = VFR ) VFR_Output . SetCFR ( fps ) ;
}
else provider - > OverrideFrameTimeList ( temp . GetFrameTimeList ( ) ) ;
// Gather video parameters
length = provider - > GetFrameCount ( ) ;
w = provider - > GetWidth ( ) ;
h = provider - > GetHeight ( ) ;
// Set filename
videoName = filename ;
Options . AddToRecentList ( filename , _T ( " Recent vid " ) ) ;
2007-06-21 00:23:55 +02:00
wxFileName fn ( filename ) ;
StandardPaths : : SetPathValue ( _T ( " ?video " ) , fn . GetPath ( ) ) ;
2007-01-21 07:30:19 +01:00
// Get frame
frame_n = 0 ;
2007-01-23 21:50:41 +01:00
//UpdateDisplays(true);
2007-01-21 07:30:19 +01:00
Refresh ( true , true ) ;
2007-07-29 11:06:38 +02:00
// Show warning
wxString warning = provider - > GetWarning ( ) ;
if ( ! warning . IsEmpty ( ) ) wxMessageBox ( warning , _T ( " Warning " ) , wxICON_WARNING | wxOK ) ;
2007-01-21 07:30:19 +01:00
}
catch ( wxString & e ) {
wxMessageBox ( e , _T ( " Error setting video " ) , wxICON_ERROR | wxOK ) ;
}
}
loaded = provider ! = NULL ;
}
///////////////////
// Add new display
void VideoContext : : AddDisplay ( VideoDisplay * display ) {
for ( std : : list < VideoDisplay * > : : iterator cur = displayList . begin ( ) ; cur ! = displayList . end ( ) ; cur + + ) {
if ( ( * cur ) = = display ) return ;
}
displayList . push_back ( display ) ;
}
//////////////////
// Remove display
void VideoContext : : RemoveDisplay ( VideoDisplay * display ) {
displayList . remove ( display ) ;
}
///////////////////
// Update displays
void VideoContext : : UpdateDisplays ( bool full ) {
for ( std : : list < VideoDisplay * > : : iterator cur = displayList . begin ( ) ; cur ! = displayList . end ( ) ; cur + + ) {
2007-01-23 05:42:08 +01:00
// Get display
2007-01-21 07:30:19 +01:00
VideoDisplay * display = * cur ;
2007-01-23 05:42:08 +01:00
// Update slider
2007-01-21 07:30:19 +01:00
if ( full ) {
display - > UpdateSize ( ) ;
display - > ControlSlider - > SetRange ( 0 , GetLength ( ) - 1 ) ;
}
display - > ControlSlider - > SetValue ( GetFrameN ( ) ) ;
2007-01-23 05:42:08 +01:00
//display->ControlSlider->Update();
2007-01-21 07:30:19 +01:00
display - > UpdatePositionDisplay ( ) ;
2007-01-23 05:42:08 +01:00
// If not shown, don't update the display itself
if ( ! display - > IsShownOnScreen ( ) ) continue ;
2007-07-01 09:09:37 +02:00
// Update visual controls
if ( display - > visual ) display - > visual - > Refresh ( ) ;
2007-01-23 05:42:08 +01:00
// Update controls
2007-07-01 09:09:37 +02:00
//display->Refresh();
//display->Update();
display - > Render ( ) ;
2007-01-21 07:30:19 +01:00
}
2008-01-14 02:18:24 +01:00
// Update audio display
if ( audio & & audio - > loaded ) {
if ( Options . AsBool ( _T ( " Audio Draw Video Position " ) ) ) {
audio - > UpdateImage ( false ) ;
}
}
2007-01-21 07:30:19 +01:00
}
/////////////////////
// Refresh subtitles
void VideoContext : : Refresh ( bool video , bool subtitles ) {
// Reset frame
lastFrame = - 1 ;
2007-07-27 06:50:15 +02:00
// Update subtitles
2007-01-21 07:30:19 +01:00
if ( subtitles & & subsProvider ) {
2007-07-27 06:50:15 +02:00
// Re-export
2007-01-21 07:30:19 +01:00
AssExporter exporter ( grid - > ass ) ;
exporter . AddAutoFilters ( ) ;
subsProvider - > LoadSubtitles ( exporter . ExportTransform ( ) ) ;
}
// Jump to frame
JumpToFrame ( frame_n ) ;
}
///////////////////////////////////////
// Jumps to a frame and update display
void VideoContext : : JumpToFrame ( int n ) {
// Loaded?
if ( ! loaded ) return ;
// Prevent intervention during playback
if ( isPlaying & & n ! = playNextFrame ) return ;
2007-04-08 01:45:46 +02:00
// Threaded
2007-07-27 06:50:15 +02:00
if ( threaded & & false ) { // Doesn't work, so it's disabled
2007-04-08 01:45:46 +02:00
wxMutexLocker lock ( vidMutex ) ;
threadNextFrame = n ;
if ( ! threadLocked ) {
threadLocked = true ;
thread = new VideoContextThread ( this ) ;
thread - > Create ( ) ;
thread - > Run ( ) ;
}
}
// Not threaded
else {
// Set frame number
frame_n = n ;
GetFrameAsTexture ( n ) ;
2007-01-21 07:30:19 +01:00
2007-04-08 01:45:46 +02:00
// Display
UpdateDisplays ( false ) ;
2007-01-21 07:30:19 +01:00
2007-04-08 01:45:46 +02:00
// Update grid
if ( ! isPlaying & & Options . AsBool ( _T ( " Highlight subs in frame " ) ) ) grid - > Refresh ( false ) ;
}
2007-01-21 07:30:19 +01:00
}
////////////////////////////
// Jumps to a specific time
2007-04-07 05:12:55 +02:00
void VideoContext : : JumpToTime ( int ms , bool exact ) {
int frame ;
if ( exact ) frame = VFR_Output . PFrameAtTime ( ms ) ;
else frame = VFR_Output . GetFrameAtTime ( ms ) ;
JumpToFrame ( frame ) ;
2007-01-21 07:30:19 +01:00
}
//////////////////
// Get GL context
wxGLContext * VideoContext : : GetGLContext ( wxGLCanvas * canvas ) {
2007-09-12 01:22:26 +02:00
# ifdef __WXMAC__
// This is probably wrong...
if ( ! glContext ) glContext = new wxGLContext ( 0 , canvas , wxPalette ( ) , 0 ) ;
# else
2007-01-21 07:30:19 +01:00
if ( ! glContext ) glContext = new wxGLContext ( canvas ) ;
2007-09-12 01:22:26 +02:00
# endif
2007-01-21 07:30:19 +01:00
return glContext ;
2008-01-13 06:57:09 +01:00
/*
if ( ! ( ( VideoDisplay * ) canvas ) - > freeSize ) return glContext ;
else {
static wxGLContext * test = NULL ;
if ( test = NULL ) test = new wxGLContext ( canvas , glContext ) ;
return test ;
} */
2007-01-21 07:30:19 +01:00
}
2007-04-03 04:55:43 +02:00
////////////////////////
// Requests a new frame
2007-04-08 00:03:06 +02:00
AegiVideoFrame VideoContext : : GetFrame ( int n , bool raw ) {
2007-04-03 04:55:43 +02:00
// Current frame if -1
if ( n = = - 1 ) n = frame_n ;
2007-04-08 20:12:51 +02:00
// Get available formats
int formats = FORMAT_RGB32 ;
if ( yv12shader ! = 0 | | OpenGLWrapper : : UseShaders ( ) ) formats | = FORMAT_YV12 ;
2007-04-03 04:55:43 +02:00
// Get frame
2007-04-08 20:12:51 +02:00
AegiVideoFrame frame = provider - > GetFrame ( n , formats ) ;
2007-04-03 04:55:43 +02:00
// Raster subtitles if available/necessary
2007-04-08 00:03:06 +02:00
if ( ! raw & & subsProvider & & subsProvider - > CanRaster ( ) ) {
2007-04-08 20:12:51 +02:00
tempFrame . CopyFrom ( frame ) ;
2007-04-03 04:55:43 +02:00
subsProvider - > DrawSubtitles ( tempFrame , VFR_Input . GetTimeAtFrame ( n , true , true ) / 1000.0 ) ;
return tempFrame ;
}
// Return pure frame
2007-04-08 20:12:51 +02:00
else return frame ;
2007-04-03 04:55:43 +02:00
}
2007-01-21 07:30:19 +01:00
///////////////////////////
// Get GL Texture of frame
GLuint VideoContext : : GetFrameAsTexture ( int n ) {
// Already uploaded
2007-01-22 23:57:45 +01:00
if ( n = = lastFrame | | n = = - 1 ) return lastTex ;
2007-01-21 07:30:19 +01:00
// Get frame
AegiVideoFrame frame = GetFrame ( n ) ;
// Set frame
lastFrame = n ;
2007-04-03 04:31:51 +02:00
// Set context
2007-09-12 01:22:26 +02:00
# ifdef __APPLE__
GetGLContext ( displayList . front ( ) ) - > SetCurrent ( ) ;
# else
2007-04-03 04:31:51 +02:00
GetGLContext ( displayList . front ( ) ) - > SetCurrent ( * displayList . front ( ) ) ;
2007-09-12 01:22:26 +02:00
# endif
2007-04-03 04:31:51 +02:00
glEnable ( GL_TEXTURE_2D ) ;
if ( glGetError ( ) ! = 0 ) throw _T ( " Error enabling texture. " ) ;
2007-01-21 07:30:19 +01:00
// Image type
2007-01-24 04:54:32 +01:00
GLenum format = GL_LUMINANCE ;
2007-04-03 04:55:43 +02:00
if ( frame . format = = FORMAT_RGB32 ) {
if ( frame . invertChannels ) format = GL_BGRA_EXT ;
2007-01-21 07:30:19 +01:00
else format = GL_RGBA ;
}
2007-04-03 04:55:43 +02:00
else if ( frame . format = = FORMAT_RGB24 ) {
if ( frame . invertChannels ) format = GL_BGR_EXT ;
2007-01-21 07:30:19 +01:00
else format = GL_RGB ;
}
2007-04-03 04:55:43 +02:00
else if ( frame . format = = FORMAT_YV12 ) {
2007-01-21 07:30:19 +01:00
format = GL_LUMINANCE ;
}
2007-04-03 04:55:43 +02:00
isInverted = frame . flipped ;
2007-01-21 07:30:19 +01:00
if ( lastTex = = 0 ) {
// Enable
glShadeModel ( GL_FLAT ) ;
// Generate texture with GL
2007-01-27 07:15:25 +01:00
//glActiveTexture(GL_TEXTURE0);
2007-01-21 07:30:19 +01:00
glGenTextures ( 1 , & lastTex ) ;
2007-01-22 21:01:07 +01:00
if ( glGetError ( ) ! = 0 ) throw _T ( " Error generating texture. " ) ;
2007-01-21 07:30:19 +01:00
glBindTexture ( GL_TEXTURE_2D , lastTex ) ;
2007-01-22 21:01:07 +01:00
if ( glGetError ( ) ! = 0 ) throw _T ( " Error binding texture. " ) ;
2007-01-21 07:30:19 +01:00
2007-01-22 23:57:45 +01:00
// Texture parameters
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MIN_FILTER , GL_NEAREST ) ;
2007-03-28 17:24:06 +02:00
if ( glGetError ( ) ! = 0 ) throw _T ( " Error setting min_filter texture parameter. " ) ;
2007-01-22 23:57:45 +01:00
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MAG_FILTER , GL_NEAREST ) ;
2007-03-28 17:24:06 +02:00
if ( glGetError ( ) ! = 0 ) throw _T ( " Error setting mag_filter texture parameter. " ) ;
2007-01-22 23:57:45 +01:00
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_WRAP_S , GL_CLAMP ) ;
2007-03-28 17:24:06 +02:00
if ( glGetError ( ) ! = 0 ) throw _T ( " Error setting wrap_s texture parameter. " ) ;
2007-01-22 23:57:45 +01:00
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_WRAP_T , GL_CLAMP ) ;
2007-03-28 17:24:06 +02:00
if ( glGetError ( ) ! = 0 ) throw _T ( " Error setting wrap_t texture parameter. " ) ;
2007-01-22 23:57:45 +01:00
2007-04-13 05:52:25 +02:00
// Allocate texture
2007-04-03 04:55:43 +02:00
int height = frame . h ;
if ( frame . format = = FORMAT_YV12 ) height = height * 3 / 2 ;
int tw = SmallestPowerOf2 ( MAX ( frame . pitch [ 0 ] / frame . GetBpp ( 0 ) , frame . pitch [ 1 ] + frame . pitch [ 2 ] ) ) ;
2007-01-27 07:15:25 +01:00
int th = SmallestPowerOf2 ( height ) ;
2007-03-28 17:24:06 +02:00
glTexImage2D ( GL_TEXTURE_2D , 0 , GL_RGBA8 , tw , th , 0 , format , GL_UNSIGNED_BYTE , NULL ) ;
if ( glGetError ( ) ! = 0 ) {
tw = MAX ( tw , th ) ;
th = tw ;
glTexImage2D ( GL_TEXTURE_2D , 0 , GL_RGBA8 , tw , th , 0 , format , GL_UNSIGNED_BYTE , NULL ) ;
if ( glGetError ( ) ! = 0 ) {
glTexImage2D ( GL_TEXTURE_2D , 0 , GL_RGBA , tw , th , 0 , format , GL_UNSIGNED_BYTE , NULL ) ;
if ( glGetError ( ) ! = 0 ) throw _T ( " Error allocating texture. " ) ;
}
}
2007-04-03 04:55:43 +02:00
texW = float ( frame . w ) / float ( tw ) ;
texH = float ( frame . h ) / float ( th ) ;
2007-01-21 07:30:19 +01:00
// Set texture
2007-01-22 23:57:45 +01:00
//glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST);
2007-04-03 04:31:51 +02:00
//if (glGetError() != 0) throw _T("Error setting hinting.");
// Create shader
2007-04-03 04:55:43 +02:00
if ( frame . format = = FORMAT_YV12 & & yv12shader = = 0 & & OpenGLWrapper : : UseShaders ( ) ) {
yv12shader = OpenGLWrapper : : CreateYV12Shader ( texW , texH , float ( frame . pitch [ 1 ] ) / float ( tw ) ) ;
2007-04-03 04:31:51 +02:00
}
2007-01-22 23:57:45 +01:00
// Set priority
float priority = 1.0f ;
glPrioritizeTextures ( 1 , & lastTex , & priority ) ;
2007-01-21 07:30:19 +01:00
}
// Load texture data
2007-04-13 22:21:37 +02:00
glBindTexture ( GL_TEXTURE_2D , lastTex ) ;
2007-04-03 04:55:43 +02:00
glTexSubImage2D ( GL_TEXTURE_2D , 0 , 0 , 0 , frame . pitch [ 0 ] / frame . GetBpp ( 0 ) , frame . h , format , GL_UNSIGNED_BYTE , frame . data [ 0 ] ) ;
2007-01-22 21:01:07 +01:00
if ( glGetError ( ) ! = 0 ) throw _T ( " Error uploading primary plane " ) ;
2007-01-21 07:30:19 +01:00
// UV planes for YV12
2007-04-03 04:55:43 +02:00
if ( frame . format = = FORMAT_YV12 ) {
2007-01-29 06:47:29 +01:00
int u = 1 ;
int v = 2 ;
2007-04-03 04:55:43 +02:00
if ( frame . invertChannels ) {
2007-01-29 06:47:29 +01:00
u = 2 ;
v = 1 ;
}
2007-04-03 04:55:43 +02:00
glTexSubImage2D ( GL_TEXTURE_2D , 0 , 0 , frame . h , frame . pitch [ 1 ] , frame . h / 2 , format , GL_UNSIGNED_BYTE , frame . data [ u ] ) ;
2007-01-22 21:01:07 +01:00
if ( glGetError ( ) ! = 0 ) throw _T ( " Error uploading U plane. " ) ;
2007-04-03 04:55:43 +02:00
glTexSubImage2D ( GL_TEXTURE_2D , 0 , frame . pitch [ 1 ] , frame . h , frame . pitch [ 2 ] , frame . h / 2 , format , GL_UNSIGNED_BYTE , frame . data [ v ] ) ;
2007-01-22 21:01:07 +01:00
if ( glGetError ( ) ! = 0 ) throw _T ( " Error uploadinv V plane. " ) ;
2007-01-21 07:30:19 +01:00
}
// Return texture number
return lastTex ;
}
/////////////////
// Save snapshot
2007-04-08 00:03:06 +02:00
void VideoContext : : SaveSnapshot ( bool raw ) {
2007-01-21 07:30:19 +01:00
// Get folder
wxString option = Options . AsText ( _ ( " Video Screenshot Path " ) ) ;
wxFileName videoFile ( videoName ) ;
wxString basepath ;
2008-01-10 23:28:47 +01:00
// Is it a path specifier and not an actual fixed path?
2007-06-21 02:46:50 +02:00
if ( option [ 0 ] = = _T ( ' ? ' ) ) {
2008-01-10 23:28:47 +01:00
// If dummy video is loaded, we can't save to the video location
2008-01-11 00:22:49 +01:00
if ( option . StartsWith ( _T ( " ?video " ) ) & & ( videoName . Find ( _T ( " ?dummy " ) ) ! = wxNOT_FOUND ) ) {
2008-01-10 23:28:47 +01:00
// So try the script location instead
option = _T ( " ?script " ) ;
}
// Find out where the ?specifier points to
2007-06-21 02:46:50 +02:00
basepath = StandardPaths : : DecodePath ( option ) ;
2008-01-10 23:28:47 +01:00
// If whereever that is isn't defined, we can't save there
if ( ( basepath = = _T ( " \\ " ) ) | | ( basepath = = _T ( " / " ) ) ) {
// 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
2007-06-21 02:46:50 +02:00
else basepath = DecodeRelativePath ( option , StandardPaths : : DecodePath ( _T ( " ?user/ " ) ) ) ;
2007-01-21 07:30:19 +01:00
basepath + = _T ( " / " ) + videoFile . GetName ( ) ;
// Get full path
int session_shot_count = 1 ;
wxString path ;
while ( 1 ) {
path = basepath + wxString : : Format ( _T ( " _%03i_%i.png " ) , session_shot_count , frame_n ) ;
+ + session_shot_count ;
wxFileName tryPath ( path ) ;
if ( ! tryPath . FileExists ( ) ) break ;
}
// Save
2007-04-08 00:03:06 +02:00
GetFrame ( frame_n , raw ) . GetImage ( ) . SaveFile ( path , wxBITMAP_TYPE_PNG ) ;
2007-01-21 07:30:19 +01:00
}
////////////////////////////
// Get dimensions of script
void VideoContext : : GetScriptSize ( int & sw , int & sh ) {
grid - > ass - > GetResolution ( sw , sh ) ;
}
////////
// Play
void VideoContext : : Play ( ) {
// Stop if already playing
if ( isPlaying ) {
Stop ( ) ;
return ;
}
// Set variables
startFrame = frame_n ;
endFrame = - 1 ;
// Start playing audio
audio - > Play ( VFR_Output . GetTimeAtFrame ( startFrame ) , - 1 ) ;
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
startTime = clock ( ) ;
playTime = startTime ;
playback . SetOwner ( this , VIDEO_PLAY_TIMER ) ;
playback . Start ( 1 ) ;
}
/////////////
// Play line
void VideoContext : : PlayLine ( ) {
// Get line
AssDialogue * curline = grid - > GetDialogue ( grid - > editBox - > linen ) ;
if ( ! curline ) return ;
// Start playing audio
audio - > Play ( curline - > Start . GetMS ( ) , curline - > End . GetMS ( ) ) ;
// Set variables
isPlaying = true ;
startFrame = VFR_Output . GetFrameAtTime ( curline - > Start . GetMS ( ) , true ) ;
endFrame = VFR_Output . GetFrameAtTime ( curline - > End . GetMS ( ) , false ) ;
// Jump to start
playNextFrame = startFrame ;
JumpToFrame ( startFrame ) ;
// Set other variables
startTime = clock ( ) ;
playTime = startTime ;
// Start timer
playback . SetOwner ( this , VIDEO_PLAY_TIMER ) ;
playback . Start ( 1 ) ;
}
////////
// Stop
void VideoContext : : Stop ( ) {
if ( isPlaying ) {
playback . Stop ( ) ;
isPlaying = false ;
audio - > Stop ( ) ;
}
}
//////////////
// Play timer
void VideoContext : : OnPlayTimer ( wxTimerEvent & event ) {
// Lock
wxMutexError res = playMutex . TryLock ( ) ;
if ( res = = wxMUTEX_BUSY ) return ;
playMutex . Unlock ( ) ;
wxMutexLocker lock ( playMutex ) ;
// Get time difference
clock_t cur = clock ( ) ;
clock_t dif = ( clock ( ) - startTime ) * 1000 / CLOCKS_PER_SEC ;
playTime = cur ;
// Find next frame
int startMs = VFR_Output . GetTimeAtFrame ( startFrame ) ;
int nextFrame = frame_n ;
int i = 0 ;
for ( i = 0 ; i < 10 ; i + + ) {
if ( nextFrame > = length ) break ;
if ( dif < VFR_Output . GetTimeAtFrame ( nextFrame ) - startMs ) {
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
2007-06-02 15:48:36 +02:00
if ( keepAudioSync & & ( nextFrame < frame_n | | nextFrame > frame_n + 2 ) ) audio - > player - > SetCurrentPosition ( audio - > GetSampleAtMS ( VFR_Output . GetTimeAtFrame ( nextFrame ) ) ) ;
2007-01-21 07:30:19 +01:00
// Jump to next frame
playNextFrame = nextFrame ;
frame_n = nextFrame ;
JumpToFrame ( nextFrame ) ;
// Sync audio
2007-07-30 06:05:45 +02:00
if ( keepAudioSync & & nextFrame % 10 = = 0 & & audio & & audio - > provider & & audio - > player ) {
2007-08-31 16:11:35 +02:00
int64_t audPos = audio - > GetSampleAtMS ( VFR_Output . GetTimeAtFrame ( nextFrame ) ) ;
int64_t curPos = audio - > player - > GetCurrentPosition ( ) ;
2007-01-21 07:30:19 +01:00
int delta = int ( audPos - curPos ) ;
if ( delta < 0 ) delta = - delta ;
int maxDelta = audio - > provider - > GetSampleRate ( ) ;
if ( delta > maxDelta ) audio - > player - > SetCurrentPosition ( audPos ) ;
}
}
//////////////////////////////
// Get name of temp work file
wxString VideoContext : : GetTempWorkFile ( ) {
if ( tempfile . IsEmpty ( ) ) {
tempfile = wxFileName : : CreateTempFileName ( _T ( " aegisub " ) ) ;
wxRemoveFile ( tempfile ) ;
tempfile + = _T ( " .ass " ) ;
}
return tempfile ;
}
/////////////////
// Get keyframes
wxArrayInt VideoContext : : GetKeyFrames ( ) {
if ( OverKeyFramesLoaded ( ) ) return overKeyFrames ;
return KeyFrames ;
}
/////////////////
// Set keyframes
void VideoContext : : SetKeyFrames ( wxArrayInt frames ) {
KeyFrames = frames ;
}
/////////////////////////
// Set keyframe override
void VideoContext : : SetOverKeyFrames ( wxArrayInt frames ) {
overKeyFrames = frames ;
overKeyFramesLoaded = true ;
}
///////////////////
// Close keyframes
void VideoContext : : CloseOverKeyFrames ( ) {
overKeyFrames . Clear ( ) ;
overKeyFramesLoaded = false ;
}
//////////////////////////////////////////
// Check if override keyframes are loaded
bool VideoContext : : OverKeyFramesLoaded ( ) {
return overKeyFramesLoaded ;
}
/////////////////////////////////
// Check if keyframes are loaded
bool VideoContext : : KeyFramesLoaded ( ) {
return overKeyFramesLoaded | | keyFramesLoaded ;
}
2007-01-23 07:32:16 +01:00
//////////////////////////
// Calculate aspect ratio
double VideoContext : : GetARFromType ( int type ) {
if ( type = = 0 ) return ( double ) VideoContext : : Get ( ) - > GetWidth ( ) / ( double ) VideoContext : : Get ( ) - > GetHeight ( ) ;
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
}
/////////////////////
// Sets aspect ratio
void VideoContext : : SetAspectRatio ( int _type , double value ) {
// Get value
if ( _type ! = 4 ) value = GetARFromType ( _type ) ;
if ( value < 0.5 ) value = 0.5 ;
if ( value > 5.0 ) value = 5.0 ;
// Set
arType = _type ;
arValue = value ;
UpdateDisplays ( true ) ;
}
2007-01-27 07:15:25 +01:00
////////////////////////////
// Enable or disable shader
void VideoContext : : SetShader ( bool enabled ) {
OpenGLWrapper : : SetShader ( enabled ? yv12shader : 0 ) ;
}
2007-04-08 00:03:06 +02:00
////////////////////////////////////////////////
// Can draw subtitles independently from video?
bool VideoContext : : HasIndependentSubs ( ) {
return subsProvider & & subsProvider - > CanRaster ( ) ;
}
2007-04-08 01:45:46 +02:00
//////////////////////
// Thread constructor
VideoContextThread : : VideoContextThread ( VideoContext * par )
: wxThread ( wxTHREAD_DETACHED )
{
parent = par ;
}
//////////////////////
// Thread entry point
wxThread : : ExitCode VideoContextThread : : Entry ( ) {
// Set up thread
int frame = parent - > threadNextFrame ;
int curFrame = parent - > frame_n ;
bool highSubs = Options . AsBool ( _T ( " Highlight subs in frame " ) ) ;
// Loop while there is work to do
while ( true ) {
// Get frame and set frame number
2007-04-13 02:44:46 +02:00
{
wxMutexLocker glLock ( OpenGLWrapper : : glMutex ) ;
parent - > GetFrameAsTexture ( frame ) ;
parent - > frame_n = frame ;
}
2007-04-08 01:45:46 +02:00
// Display
parent - > UpdateDisplays ( false ) ;
// Update grid
if ( ! parent - > isPlaying & & highSubs ) parent - > grid - > Refresh ( false ) ;
// Get lock and check if there is more to do
wxMutexLocker lock ( parent - > vidMutex ) ;
curFrame = parent - > frame_n ;
frame = parent - > threadNextFrame ;
// Work done, kill thread and release context
if ( curFrame = = frame ) {
parent - > threadLocked = false ;
parent - > threadNextFrame = - 1 ;
Delete ( ) ;
return 0 ;
}
}
}