The big VideoDisplay split

Originally committed to SVN as r20.
This commit is contained in:
Fredrik Mellbin 2006-01-22 12:44:53 +00:00
parent cb573429fb
commit 227febdd24
8 changed files with 480 additions and 672 deletions

View File

@ -191,7 +191,7 @@ int DialogProperties::SetInfoIfDifferent(wxString key,wxString value) {
//////////////////////////
// Set res to match video
void DialogProperties::OnSetFromVideo(wxCommandEvent &event) {
ResX->SetValue(wxString::Format(_T("%i"),vid->orig_w));
ResY->SetValue(wxString::Format(_T("%i"),vid->orig_h));
ResX->SetValue(wxString::Format(_T("%i"),vid->provider->GetSourceWidth())); //fix me, null check?
ResY->SetValue(wxString::Format(_T("%i"),vid->provider->GetSourceHeight())); //fix me, identical to other set?
event.Skip();
}

View File

@ -232,8 +232,8 @@ void DialogResample::OnResample (wxCommandEvent &event) {
/////////////////////////////////////////
// Get destination resolution from video
void DialogResample::OnGetDestRes (wxCommandEvent &event) {
ResX->SetValue(wxString::Format(_T("%i"),vid->orig_w));
ResY->SetValue(wxString::Format(_T("%i"),vid->orig_h));
ResX->SetValue(wxString::Format(_T("%i"),vid->provider->GetSourceWidth())); //fix me, null check?
ResY->SetValue(wxString::Format(_T("%i"),vid->provider->GetSourceHeight()));
}

View File

@ -465,8 +465,7 @@ void FrameMain::LoadSubtitles (wxString filename,wxString charset) {
}
// Setup
bool isFile = true;
if (filename == _T("")) isFile = false;
bool isFile = (filename != _T(""));
// Load
try {
@ -513,7 +512,7 @@ void FrameMain::LoadSubtitles (wxString filename,wxString charset) {
SynchronizeProject(true);
// Update video
videoBox->videoDisplay->SetSubtitles(filename);
//videoBox->videoDisplay->SetSubtitles(filename); //fix me, remove?
// Update title bar
UpdateTitle();
@ -523,9 +522,6 @@ void FrameMain::LoadSubtitles (wxString filename,wxString charset) {
//////////////////
// Save subtitles
bool FrameMain::SaveSubtitles(bool saveas,bool withCharset) {
// Synchronize
SynchronizeProject();
// Try to get filename from file
wxString filename;
if (saveas == false && AssFile::top->IsASS) filename = AssFile::top->filename;
@ -534,8 +530,12 @@ bool FrameMain::SaveSubtitles(bool saveas,bool withCharset) {
if (filename == _T("")) {
videoBox->videoDisplay->Stop();
filename = wxFileSelector(_("Save subtitles file"),_T(""),_T(""),_T(""),_T("Advanced Substation Alpha (*.ass)|*.ass"),wxSAVE | wxOVERWRITE_PROMPT,this);
AssFile::top->filename = filename; //fix me, ghetto hack for correct relative path generation in SynchronizeProject()
}
// Synchronize
SynchronizeProject();
// Actually save
if (!filename.empty()) {
// Get charset
@ -548,7 +548,7 @@ bool FrameMain::SaveSubtitles(bool saveas,bool withCharset) {
// Save
try {
videoBox->videoDisplay->SetSubtitles(filename);
//videoBox->videoDisplay->SetSubtitles(filename); fix me, remove?
AssFile::top->Save(filename,true,true,charset);
UpdateTitle();
}
@ -641,7 +641,6 @@ void FrameMain::SetDisplayMode(int mode) {
// Update
curMode = mode;
UpdateToolbar();
videoBox->videoDisplay->RefreshVideo();
videoBox->VideoSizer->Layout();
MainSizer->Layout();
//Layout();
@ -760,8 +759,6 @@ void FrameMain::SynchronizeProject(bool fromSubs) {
// Video
if (curSubsVideo != videoBox->videoDisplay->videoName) {
videoBox->videoDisplay->Locked(true);
videoBox->videoDisplay->Reset();
if (curSubsVideo != _T("")) {
LoadVideo(curSubsVideo);
if (videoBox->videoDisplay->loaded) {
@ -770,7 +767,6 @@ void FrameMain::SynchronizeProject(bool fromSubs) {
videoBox->videoDisplay->SetZoomPos(videoZoom-1);
}
}
videoBox->videoDisplay->Locked(false);
}
// Audio
@ -851,7 +847,7 @@ void FrameMain::LoadVideo(wxString file,bool autoload) {
if (videoBox->videoDisplay->loaded) {
int scriptx = SubsBox->ass->GetScriptInfoAsInt(_T("PlayResX"));
int scripty = SubsBox->ass->GetScriptInfoAsInt(_T("PlayResY"));
int vidx = videoBox->videoDisplay->orig_w, vidy = videoBox->videoDisplay->orig_h;
int vidx = videoBox->videoDisplay->provider->GetSourceWidth(), vidy = videoBox->videoDisplay->provider->GetSourceHeight();
if (scriptx != vidx || scripty != vidy) {
switch (Options.AsInt(_T("Video Check Script Res"))) {
case 1:
@ -929,7 +925,6 @@ void FrameMain::LoadVFR(wxString filename) {
SubsBox->CommitChanges();
EditBox->UpdateFrameTiming();
//SynchronizeProject();
}

View File

@ -1640,7 +1640,9 @@ void SubtitlesGrid::CommitChanges(bool force) {
// Export
wxString workfile = GetTempWorkFile();
ass->Export(workfile);
video->RefreshVideo(true);
if (video->loaded)
video->RefreshSubtitles();
// Resume play
if (playing) video->Play();

View File

@ -37,7 +37,6 @@
////////////
// Includes
#include "video_display.h"
#include "avisynth_wrap.h"
#include "vfr.h"
#include "ass_file.h"
#include "ass_time.h"
@ -86,22 +85,18 @@ END_EVENT_TABLE()
VideoDisplay::VideoDisplay(wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style, const wxString& name)
: wxWindow (parent, id, pos, size, style, name)
{
provider = NULL;
curLine = NULL;
curFrame = NULL;
backbuffer = NULL;
ControlSlider = NULL;
PositionDisplay = NULL;
loaded = false;
frame_n = 0;
origSize = size;
zoom = 0.75;
arType = 0;
isLocked = false;
gettingFrame = false;
IsPlaying = false;
threaded = Options.AsBool(_T("Threaded Video"));
nextFrame = -1;
framesSkipped = 0;
// Create PNG handler
wxPNGHandler *png = new wxPNGHandler;
@ -116,476 +111,108 @@ VideoDisplay::VideoDisplay(wxWindow* parent, wxWindowID id, const wxPoint& pos,
//////////////
// Destructor
VideoDisplay::~VideoDisplay () {
ControlSlider = NULL;
Unload();
if (backbuffer) delete backbuffer;
backbuffer = NULL;
SetVideo(_T(""));
delete backbuffer;
}
///////////////////////
// Sets video filename
void VideoDisplay::SetVideo(const wxString &filename) {
try {
if (!filename.empty()) {
// Verify if file exists
wxFileName filetest(filename);
if (!filetest.FileExists()) throw _T("File not found.");
// OK to load
if (videoName != filename) {
Unload();
videoName = filename;
OpenAVSVideo();
if (!loaded) Reset();
else RefreshVideo();
// Set keyframes
if (filename.Right(4).Lower() == _T(".avi")) KeyFrames = VFWWrapper::GetKeyFrames(filename);
else KeyFrames.Clear();
// Add to recent
Options.AddToRecentList(filename,_T("Recent vid"));
}
else Reset();
}
else Reset();
}
catch (wchar_t *error) {
wxMessageBox(error,_T("Error setting video"),wxICON_ERROR | wxOK);
Reset();
}
catch (...) {
wxMessageBox(_T("Unhandled exception"),_T("Error setting video"),wxICON_ERROR | wxOK);
Reset();
}
}
///////////////////////
// Sets subtitles filename
void VideoDisplay::SetSubtitles(const wxString &filename) {
if (!loaded) return;
wxString old = subsName;
subsName = filename;
if (!videoName.empty()) {
OpenAVSSubs();
RefreshVideo();
}
}
////////////
// Open AVS
void VideoDisplay::OpenAVSVideo() {
if (videoName.empty()) return;
bool directShow = false;
// Create AviSynth environment
{
wxMutexLocker lock(AviSynthMutex);
AVSValue script;
// Load VSFilter
try {
env->Invoke("LoadPlugin", env->SaveString(GetVSFilter().mb_str(wxConvLocal)));
}
catch (AvisynthError &) {
throw _T("Failed opening VSfilter");
}
try {
// Prepare filename
char *videoFilename = env->SaveString(videoName.mb_str(wxConvLocal));
wxString extension = videoName.Right(4);
extension.LowerCase();
// Load depending on extension
if (extension == _T(".avs")) {
script = env->Invoke("Import", videoFilename);
} else if (extension == _T(".avi")) {
try {
const char *argnames[2] = { 0, "audio" };
AVSValue args[2] = { videoFilename, false };
script = env->Invoke("AviSource", AVSValue(args,2), argnames);
//fix me, check for video?
} catch (AvisynthError &) {
const char *argnames[3] = { 0, "video", "audio" };
AVSValue args[3] = { videoFilename, true, false };
script = env->Invoke("DirectShowSource", AVSValue(args,3),argnames);
directShow = true;
}
}
else if (extension == _T(".d2v") && env->FunctionExists("mpeg2dec3_Mpeg2Source")) //prefer mpeg2dec3
script = env->Invoke("mpeg2dec3_Mpeg2Source", videoFilename);
else if (extension == _T(".d2v")) //try other mpeg2source
script = env->Invoke("Mpeg2Source", videoFilename);
else {
const char *argnames[3] = { 0, "video", "audio" };
AVSValue args[3] = { videoFilename, true, false };
script = env->Invoke("DirectShowSource", AVSValue(args,3),argnames);
directShow = true;
}
} catch (AvisynthError &err) {
wxMessageBox (wxString(_T("AviSynth error: ")) + wxString(err.msg,wxConvLocal), _T("Error"), wxOK | wxICON_ERROR);
}
// Check if video is valid
PClip clip = script.AsClip();
VideoInfo vi = clip->GetVideoInfo();
if (!vi.HasVideo())
wxMessageBox (wxString(_T("No video found: ")), _T("Error"), wxOK | wxICON_ERROR);
// Get resolution
orig_w = vi.width;
orig_h = vi.height;
// Convert to RGB32 if needed
script = env->Invoke("ConvertToRGB32", clip);
// Cache
sublessVideo = (env->Invoke("InternalCache", script)).AsClip();
}
// Continue with subs
OpenAVSSubs();
// Issue warning if it's DirectShow
if (directShow) wxMessageBox (_("This file is being loaded using DirectShow, which has UNRELIABLE seeking. Frame numbers MIGHT NOT match display, so precise timing CANNOT be trusted."), _("Warning!"), wxOK | wxICON_WARNING);
}
///////////////////
// Opens subtitles
void VideoDisplay::OpenAVSSubs () {
// Vars
{
wxMutexLocker lock(AviSynthMutex);
AVSValue script;
video = sublessVideo;
// Make sure there is a workfile
wxString workfile = grid->GetTempWorkFile();
wxFileName file(workfile);
if (!file.FileExists()) {
grid->CommitChanges(true);
}
if (!file.FileExists()) {
throw _T("Failed creating temporary subs file. Make sure you have write permission on folder.");
}
// Insert subs
try {
PClip clip = video;
char temp[512];
strcpy(temp,workfile.mb_str(wxConvLocal));
AVSValue args1[2] = { clip, temp };
script = env->Invoke("TextSub", AVSValue(args1,2));
video = script.AsClip();
} catch (AvisynthError &err) {
wxMessageBox (wxString(_T("AviSynth error: ")) + wxString(err.msg,wxConvLocal), _T("Error"), wxOK | wxICON_ERROR);
return;
}
// Zoom & AR
if (zoom != 1.0 || arType != 0) {
try {
// Get video data
PClip clip = video;
VideoInfo vi = clip->GetVideoInfo();
// Get aspect ratio data
int pos_h = vi.height;
int pos_w = vi.width;
switch (arType) {
case 1: pos_w = pos_h * 4 / 3; break;
case 2: pos_w = pos_h * 16 / 9; break;
}
// Resize
AVSValue args[3] = { clip, int(zoom*pos_w), int(zoom*pos_h) };
script = env->Invoke(Options.AsText(_T("Video resizer")).mb_str(wxConvLocal), AVSValue(args,3));
video = script.AsClip();
} catch (AvisynthError &err) {
wxMessageBox (wxString(_T("AviSynth error: ")) + wxString(err.msg,wxConvLocal), _T("Error"), wxOK | wxICON_ERROR);
return;
}
}
// Cache
video = (env->Invoke("InternalCache", video)).AsClip();
}
// Set final
PrepareAfterAVS();
}
////////////////////////////////////////
// Sets control up after loading an AVS
void VideoDisplay::PrepareAfterAVS() {
{
// Gather video parameters
wxMutexLocker lock(AviSynthMutex);
VideoInfo vi = video->GetVideoInfo();
w = vi.width;
h = vi.height;
length = vi.num_frames;
if (vi.fps_denominator != 0) fps = double(vi.fps_numerator) / double(vi.fps_denominator);
else fps = 0;
VFR_Input.SetCFR(fps);
if (!VFR_Output.loaded) VFR_Output.SetCFR(fps,true);
void VideoDisplay::UpdateSize() {
if (provider) {
w = provider->GetWidth();
h = provider->GetHeight();
// Set the size for this control
SetClientSize(w,h);
int _w,_h;
GetSize(&_w,&_h);
SetSizeHints(_w,_h,_w,_h);
// Set range of slider
if (ControlSlider) ControlSlider->SetRange(0,vi.num_frames-1);
// Clear frame
if (curFrame) delete curFrame;
curFrame = NULL;
// Flag as loaded
loaded = true;
}
}
///////////
// Unloads
void VideoDisplay::Unload() {
{
wxMutexLocker lock(AviSynthMutex);
// AviSynth cleanup
video = NULL;
sublessVideo = NULL;
// Internal cleanup
loaded = false;
if (curFrame) delete curFrame;
curFrame = NULL;
videoName = _T("");
///////////////////////
// Sets video filename
void VideoDisplay::SetVideo(const wxString &filename) {
if (filename == _T("")) {
delete provider;
provider = NULL;
if (VFR_Output.vfr == NULL) VFR_Output.Unload();
VFR_Input.Unload();
videoName = _T("");
frame_n = 0;
if (ControlSlider) {
ControlSlider->SetRange(0,0);
Reset();
} else {
SetVideo(_T(""));
try {
grid->CommitChanges(true);
bool usedDirectshow;
provider = new VideoProvider(filename,grid->GetTempWorkFile(),0.75,usedDirectshow,true);
// Set keyframes
if (filename.Right(4).Lower() == _T(".avi"))
KeyFrames = VFWWrapper::GetKeyFrames(filename);
else
KeyFrames.Clear();
UpdateSize();
//Gather video parameters
length = provider->GetFrameCount();
fps = provider->GetFPS();
VFR_Input.SetCFR(fps);
if (!VFR_Output.loaded) VFR_Output.SetCFR(fps,true);
// Set range of slider
ControlSlider->SetRange(0,length-1);
ControlSlider->SetValue(0);
}
videoName = filename;
// Add to recent
Options.AddToRecentList(filename,_T("Recent vid"));
RefreshVideo();
} catch (wxString &e) {
wxMessageBox(e,_T("Error setting video"),wxICON_ERROR | wxOK);
}
}
loaded = provider != NULL;
}
//////////
// Resets
void VideoDisplay::Reset() {
Unload();
w = origSize.GetX();
h = origSize.GetY();
SetClientSize(w,h);
int _w,_h;
GetSize(&_w,&_h);
SetSizeHints(_w,_h,_w,_h);
}
void VideoDisplay::RefreshSubtitles() {
provider->RefreshSubtitles();
RefreshVideo();
}
//////////////////////
// Gets a frame image
void VideoDisplay::GetFrameImage(int n) {
// Prepare copy
unsigned char *data;
VideoInfo vi = video->GetVideoInfo();
PVideoFrame avsFrame;
try {
avsFrame = video->GetFrame(n,env);
}
catch (AvisynthError err) {
wxMessageBox (wxString(_T("AviSynth error: ")) + wxString(err.msg,wxConvLocal), _T("Error getting frame"), wxOK | wxICON_ERROR);
return;
}
catch (...) {
wxMessageBox(_T("AviSynth threw an exception while trying to retrieve frame."),_T("Error getting frame"),wxICON_ERROR | wxOK);
return;
}
unsigned int pitch = avsFrame->GetPitch();
unsigned int read_w = avsFrame->GetRowSize();
unsigned int read_h = avsFrame->GetHeight();
int depth = wxDisplayDepth();
int bpp = depth/8;
unsigned int x,y,dx;
// Output
data = (unsigned char*) malloc(w*h*bpp);
// RGB24
if (vi.IsRGB24()) {
if (depth == 16) {
// Get pointers
const unsigned char *read_ptr = avsFrame->GetReadPtr();
unsigned short *write_ptr = (unsigned short*) (data+(w*h*2));
unsigned char r,g,b;
for (y=0;y<read_h;y++) {
write_ptr = write_ptr - read_w/3;
for (x=0,dx=0;x<read_w;x+=3,dx++) {
r = *(read_ptr+x+2);
g = *(read_ptr+x+1);
b = *(read_ptr+x);
*(write_ptr+dx) = ((r>>3)<<11) | ((g>>2)<<5) | b>>3;
}
read_ptr = read_ptr + pitch;
}
}
if (depth == 24) {
// Get pointers
const unsigned char *read_ptr = avsFrame->GetReadPtr();
unsigned char *write_ptr = data+(w*h*3);
for (y=0;y<read_h;y++) {
write_ptr = write_ptr - read_w;
for (x=0;x<read_w;x+=3) {
*(write_ptr+x) = *(read_ptr+x);
*(write_ptr+x+1) = *(read_ptr+x+1);
*(write_ptr+x+2) = *(read_ptr+x+2);
}
read_ptr = read_ptr + pitch;
}
}
if (depth == 32) {
// Get pointers
const unsigned char *read_ptr = avsFrame->GetReadPtr();
unsigned char *write_ptr = data+(w*h*4);
unsigned int delta = pitch-read_w;
unsigned int linelen = read_w*4/3;
int wid = read_w/3;
int i,j;
for (j=read_h;--j>=0;) {
write_ptr -= linelen;
for (i=wid;--i>=0;) {
*(write_ptr++) = *(read_ptr++);
*(write_ptr++) = *(read_ptr++);
*(write_ptr++) = *(read_ptr++);
write_ptr++;
}
read_ptr = read_ptr += delta;
write_ptr -= linelen;
}
}
}
// RGB32
else if (vi.IsRGB32()) {
if (depth == 8) {
throw _T("8-bit display not supported");
}
if (depth == 15) {
throw _T("15-bit display not supported");
}
if (depth == 16) {
// Get pointers
const unsigned char *read_ptr = avsFrame->GetReadPtr();
unsigned short *write_ptr = (unsigned short*) (data+(w*h*2));
unsigned char r,g,b;
for (y=0;y<read_h;y++) {
write_ptr = write_ptr - read_w/4;
for (x=0,dx=0;x<read_w;x+=4,dx++) {
r = *(read_ptr+x+2);
g = *(read_ptr+x+1);
b = *(read_ptr+x);
*(write_ptr+dx) = ((r>>3)<<11) | ((g>>2)<<5) | b>>3;
}
read_ptr = read_ptr + pitch;
}
}
if (depth == 24) {
throw _T("24-bit display not supported");
}
if (depth == 32) {
// Get pointers
const unsigned int *read_ptr = (const unsigned int *) avsFrame->GetReadPtr();
unsigned int *write_ptr = ((unsigned int *) (data))+(w*h);
unsigned int delta = (pitch-read_w)/4;
unsigned int linelen = read_w/4;
int i;
for (i=read_h;--i>=0;) {
write_ptr -= linelen;
memcpy(write_ptr,read_ptr,read_w);
read_ptr += linelen + delta;
}
}
}
else {
throw _T("Wrong colour format.");
}
// Copy to image
BitmapMutex.Lock();
try {
if (curFrame) {
delete curFrame;
curFrame = NULL;
}
curFrame = new wxBitmap((const char*)data,w,h,depth);
}
catch (...) {}
BitmapMutex.Unlock();
free(data);
// Done
frame_n = n;
}
/////////////////
// OnPaint event
void VideoDisplay::OnPaint(wxPaintEvent& event) {
wxPaintDC dc(this);
// Try to grab frame if none
{
wxMutexLocker locker(BitmapMutex);
if (!curFrame) GetFrame(frame_n);
}
// Draw frame
if (curFrame) {
wxMutexLocker locker(BitmapMutex);
dc.BeginDrawing();
dc.DrawBitmap(*curFrame,0,0);
dc.DrawBitmap(GetFrame(frame_n),0,0);
dc.EndDrawing();
}
// Failed
else {
dc.BeginDrawing();
dc.SetBrush(*wxBLUE_BRUSH);
dc.DrawRectangle(0,0,w,h);
dc.EndDrawing();
}
}
///////////////
// Mouse stuff
@ -640,7 +267,6 @@ void VideoDisplay::OnMouseEvent(wxMouseEvent& event) {
if (needCreate) backbuffer = new wxBitmap(w,h);
// Prepare drawing
wxMutexLocker locker(BitmapMutex);
wxMemoryDC dc;
dc.SelectObject(*backbuffer);
dc.BeginDrawing();
@ -648,7 +274,7 @@ void VideoDisplay::OnMouseEvent(wxMouseEvent& event) {
dc.SetLogicalFunction(wxINVERT);
// Draw frame
dc.DrawBitmap(*curFrame,0,0);
dc.DrawBitmap(GetFrame(frame_n),0,0);
// Current position info
if (x >= 0 && x < w && y >= 0 && y < h) {
@ -703,14 +329,7 @@ void VideoDisplay::OnMouseEvent(wxMouseEvent& event) {
void VideoDisplay::OnMouseLeave(wxMouseEvent& event) {
if (IsPlaying) return;
// Refresh display
{
wxMutexLocker locker(BitmapMutex);
wxClientDC dc(this);
dc.BeginDrawing();
dc.DrawBitmap(*curFrame,0,0);
dc.EndDrawing();
}
RefreshVideo();
}
@ -724,10 +343,14 @@ void VideoDisplay::JumpToFrame(int n) {
if (IsPlaying && n != PlayNextFrame) return;
// Set frame
if (frame_n != n) GetFrame(n);
GetFrame(n);
// Display
RefreshVideo();
UpdatePositionDisplay();
// Update slider
if (ControlSlider) ControlSlider->SetValue(n);
ControlSlider->SetValue(n);
// Update grid
if (!IsPlaying && Options.AsBool(_T("Highlight subs in frame"))) grid->UpdateRowColours();
@ -744,15 +367,13 @@ void VideoDisplay::JumpToTime(int ms) {
///////////////////
// Sets zoom level
void VideoDisplay::SetZoom(double value) {
if (value != zoom) {
zoom = value;
if (loaded) {
OpenAVSSubs();
if (provider) {
provider->SetZoom(value);
UpdateSize();
RefreshVideo();
GetParent()->Layout();
}
}
}
//////////////////////
@ -768,15 +389,17 @@ void VideoDisplay::SetZoomPos(int value) {
///////////////////
// Sets zoom level
void VideoDisplay::SetAspectRatio(int value) {
if (value != arType) {
arType = value;
if (loaded) {
//GetParent()->Freeze();
OpenAVSSubs();
if (provider) {
if (value == 0)
provider->SetDAR((float)provider->GetSourceWidth()/(float)provider->GetSourceHeight());
else if (value == 1)
provider->SetDAR(4.0/3.0);
else if (value == 2)
provider->SetDAR(16.0/9.0);
UpdateSize();
RefreshVideo();
GetParent()->Layout();
//GetParent()->Thaw();
}
}
}
@ -854,21 +477,11 @@ void VideoDisplay::UpdateSubsRelativeTime() {
}
//////////////////////////
// Locks/unlocks updating
void VideoDisplay::Locked(bool state) {
bool oldState = isLocked;
isLocked = state;
if (loaded && oldState == true && isLocked == false) RefreshVideo();
}
/////////////////////
// Copy to clipboard
void VideoDisplay::OnCopyToClipboard(wxCommandEvent &event) {
if (wxTheClipboard->Open()) {
wxMutexLocker locker(BitmapMutex);
wxTheClipboard->SetData(new wxBitmapDataObject(*curFrame));
wxTheClipboard->SetData(new wxBitmapDataObject(GetFrame(frame_n)));
wxTheClipboard->Close();
}
}
@ -892,7 +505,7 @@ void VideoDisplay::SaveSnapshot() {
}
// Save
curFrame->ConvertToImage().SaveFile(path,wxBITMAP_TYPE_PNG);
GetFrame(frame_n).ConvertToImage().SaveFile(path,wxBITMAP_TYPE_PNG);
}
@ -910,153 +523,25 @@ void VideoDisplay::OnCopyCoords(wxCommandEvent &event) {
}
/////////////////////////
// Get VSFilter filename
wxString VideoDisplay::GetVSFilter() {
if (VSFilterPath.IsEmpty()) {
wxFileName vsfilterPath(AegisubApp::folderName + _T("vsfilter.dll"));
if (vsfilterPath.FileExists()) {
VSFilterPath = AegisubApp::folderName + _T("vsfilter.dll");
}
else {
wxRegKey reg(_T("HKEY_CLASSES_ROOT\\CLSID\\{9852A670-F845-491B-9BE6-EBD841B8A613}\\InprocServer32"));
if (reg.Exists()) {
reg.QueryValue(_T(""),VSFilterPath);
wxFileName file(VSFilterPath);
if (!file.FileExists()) VSFilterPath = _T("vsfilter.dll");
}
else {
VSFilterPath = _T("vsfilter.dll");
}
}
}
return VSFilterPath;
}
//////////////////
// Refresh screen
void VideoDisplay::RefreshVideo(bool force) {
// Prepare
if (isLocked) return;
// Not loaded
if (!loaded) {
wxClientDC dc(this);
dc.BeginDrawing();
dc.SetBrush(*wxBLUE_BRUSH);
dc.DrawRectangle(0,0,w,h);
dc.EndDrawing();
}
else {
// Forced
if (force) {
int n = frame_n;
frame_n = -1;
OpenAVSSubs();
GetFrame(n);
}
// No frame, try to get one
{
wxMutexLocker locker(BitmapMutex);
if (!curFrame) GetFrame(frame_n);
}
void VideoDisplay::RefreshVideo() {
// Draw frame
if (curFrame) {
wxMutexLocker locker(BitmapMutex);
wxClientDC dc(this);
dc.BeginDrawing();
dc.DrawBitmap(*curFrame,0,0);
dc.DrawBitmap(GetFrame(),0,0);
dc.EndDrawing();
//Refresh(false);
}
// Draw black
else {
wxClientDC dc(this);
dc.BeginDrawing();
dc.Clear();
dc.EndDrawing();
}
}
// Display
UpdatePositionDisplay();
}
////////////////////////
// Requests a new frame
void VideoDisplay::GetFrame(int n) {
// Make sure it's loaded
if (!loaded) {
//throw _T("Video not loaded");
return;
}
wxBitmap VideoDisplay::GetFrame(int n) {
frame_n = n;
// Threaded mode
if (threaded) {
nextFrame = n;
if (!gettingFrame || framesSkipped > 999999) {
framesSkipped = 0;
gettingFrame = true;
wxThread *thread = new GetFrameThread(this,n);
thread->Create();
thread->Run();
}
else {
framesSkipped++;
}
}
// Simple mode
else {
GetFrameImage(n);
return provider->GetFrame(n);
RefreshVideo();
}
}
////////////////////////////////
// Get frame thread constructor
GetFrameThread::GetFrameThread(VideoDisplay *parent,int n)
: wxThread(wxTHREAD_DETACHED)
{
display = parent;
image_n = n;
}
//////////////////////////
// Get Frame thread entry
wxThread::ExitCode GetFrameThread::Entry() {
// Get frame
display->gettingFrame = true;
AviSynthWrapper::AviSynthMutex.Lock();
try { display->GetFrameImage(image_n); }
catch (...) { }
AviSynthWrapper::AviSynthMutex.Unlock();
// Refresh video
//wxMutexGuiEnter();
try {
display->RefreshVideo();
}
catch (...) { }
//wxMutexGuiLeave();
display->gettingFrame = false;
if (display->nextFrame != image_n) {
display->GetFrame(display->nextFrame);
}
// Return
Exit();
return 0;
}
////////////////////////////
@ -1077,7 +562,6 @@ void VideoDisplay::GetScriptSize(int &sw,int &sh) {
// Width
temp = grid->ass->GetScriptInfo(_T("PlayResX"));
if (temp == _T("") || !temp.IsNumber()) {
//sw = orig_w * sh / orig_h;
sw = 288;
}
else {

View File

@ -42,55 +42,31 @@
// Headers
#include <wx/wxprec.h>
#include <windows.h>
#include "avisynth_wrap.h"
#include <time.h>
#include "video_provider.h"
//////////////
// Prototypes
class SubtitlesGrid;
class VideoDisplay;
class VideoSlider;
class AudioProvider;
class AudioDisplay;
class AssDialogue;
////////////////////
// Get Frame thread
class GetFrameThread: public wxThread {
private:
VideoDisplay *display;
int image_n;
public:
GetFrameThread(VideoDisplay *parent,int n);
wxThread::ExitCode Entry();
};
class VideoProvider;
//////////////
// Main class
class VideoDisplay: public wxWindow, public AviSynthWrapper {
friend class GetFrameThread;
class VideoDisplay: public wxWindow {
friend class AudioProvider;
private:
int mouse_x,mouse_y;
wxBitmap *backbuffer;
wxString subsName;
wxString VSFilterPath;
PClip sublessVideo;
PClip video;
double zoom;
wxSize origSize;
bool isLocked;
bool threaded;
bool gettingFrame;
int nextFrame;
int framesSkipped;
wxMutex BitmapMutex;
clock_t PlayTime;
clock_t StartTime;
@ -99,12 +75,10 @@ private:
int EndFrame;
int PlayNextFrame;
void GetFrameImage(int n);
void GetFrame(int n);
void FrameReady();
wxBitmap GetFrame(int n);
wxBitmap GetFrame() { return GetFrame(frame_n); };
void SetZoom(double value);
wxString GetVSFilter();
void UpdateSize();
void SaveSnapshot();
void OnPaint(wxPaintEvent& event);
@ -116,20 +90,19 @@ private:
void OnPlayTimer(wxTimerEvent &event);
public:
wxBitmap *curFrame;
VideoProvider *provider;
wxArrayInt KeyFrames;
SubtitlesGrid *grid;
wxString videoName;
int arType;
int w,h;
int orig_w,orig_h;
int frame_n;
int length;
bool loaded;
bool IsPlaying;
double fps;
VideoSlider *ControlSlider;
//wxSlider *zoomSlider;
wxComboBox *zoomBox;
wxTextCtrl *PositionDisplay;
wxTextCtrl *SubsPosition;
@ -139,21 +112,18 @@ public:
VideoDisplay(wxWindow* parent, wxWindowID id, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = 0, const wxString& name = wxPanelNameStr);
~VideoDisplay();
void OpenAVSVideo();
void OpenAVSSubs();
void PrepareAfterAVS();
void SetVideo(const wxString &filename);
void SetSubtitles(const wxString &filename);
void Reset();
void Unload();
void JumpToFrame(int n);
void JumpToTime(int ms);
void RefreshVideo(bool force=false);
void RefreshSubtitles();
void RefreshVideo();
void UpdatePositionDisplay();
void SetAspectRatio(int type);
void SetZoom(double value);
int GetAspectRatio() { return arType; }
void SetZoomPos(int pos);
void Locked(bool state);
void UpdateSubsRelativeTime();
void GetScriptSize(int &w,int &h);

257
core/video_provider.cpp Normal file
View File

@ -0,0 +1,257 @@
// Copyright (c) 2006, Fredrik Mellbin
// 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
//
#include <wx/filename.h>
#include <wx/msw/registry.h>
#include "video_provider.h"
#include "options.h"
#include "main.h"
VideoProvider::VideoProvider(wxString _filename, wxString _subfilename, double _zoom, bool &usedDirectshow, bool mpeg2dec3_priority) {
RGB32Video = NULL;
SubtitledVideo = NULL;
ResizedVideo = NULL;
data = NULL;
last_fnum = -1;
subfilename = _subfilename;
zoom = _zoom;
LoadVSFilter();
RGB32Video = OpenVideo(_filename,usedDirectshow,mpeg2dec3_priority);
dar = GetSourceWidth()/(double)GetSourceHeight();
SubtitledVideo = ApplySubtitles(subfilename, RGB32Video);
ResizedVideo = ApplyDARZoom(zoom, dar, SubtitledVideo);
}
VideoProvider::~VideoProvider() {
RGB32Video = NULL;
SubtitledVideo = NULL;
ResizedVideo = NULL;
delete data;
}
void VideoProvider::RefreshSubtitles() {
ResizedVideo = NULL;
SubtitledVideo = NULL;
SubtitledVideo = ApplySubtitles(subfilename, RGB32Video);
ResizedVideo = ApplyDARZoom(zoom,dar,SubtitledVideo);
GetFrame(last_fnum,true);
}
void VideoProvider::SetDAR(double _dar) {
dar = _dar;
ResizedVideo = NULL;
delete data;
data = NULL;
ResizedVideo = ApplyDARZoom(zoom,dar,SubtitledVideo);
GetFrame(last_fnum,true);
}
void VideoProvider::SetZoom(double _zoom) {
zoom = _zoom;
ResizedVideo = NULL;
delete data;
data = NULL;
ResizedVideo = ApplyDARZoom(zoom,dar,SubtitledVideo);
GetFrame(last_fnum,true);
}
PClip VideoProvider::OpenVideo(wxString _filename, bool &usedDirectshow, bool mpeg2dec3_priority) {
wxMutexLocker lock(AviSynthMutex);
AVSValue script;
usedDirectshow = false;
wxString extension = _filename.Right(4);
extension.LowerCase();
try {
// Prepare filename
char *videoFilename = env->SaveString(_filename.mb_str(wxConvLocal));
// Load depending on extension
if (extension == _T(".avs")) {
script = env->Invoke("Import", videoFilename);
} else if (extension == _T(".avi")) {
try {
const char *argnames[2] = { 0, "audio" };
AVSValue args[2] = { videoFilename, false };
script = env->Invoke("AviSource", AVSValue(args,2), argnames);
//fix me, check for video?
} catch (AvisynthError &) {
goto directshowOpen;
}
}
else if (extension == _T(".d2v") && env->FunctionExists("mpeg2dec3_Mpeg2Source") && mpeg2dec3_priority) //prefer mpeg2dec3
script = env->Invoke("mpeg2dec3_Mpeg2Source", videoFilename);
else if (extension == _T(".d2v") && env->FunctionExists("Mpeg2Source")) //try other mpeg2source
script = env->Invoke("Mpeg2Source", videoFilename);
else {
directshowOpen:
if (env->FunctionExists("DirectShowSource")) {
const char *argnames[3] = { 0, "video", "audio" };
AVSValue args[3] = { videoFilename, true, false };
script = env->Invoke("DirectShowSource", AVSValue(args,3), argnames);
usedDirectshow = true;
} else
throw AvisynthError("No function suitable for opening the video found");
}
} catch (AvisynthError &err) {
throw _T("AviSynth error: ") + wxString(err.msg,wxConvLocal);
}
if (!script.AsClip()->GetVideoInfo().HasVideo())
throw _T("No usable video found in ") + _filename;
// Convert to RGB32
script = env->Invoke("ConvertToRGB32", script);
// Cache
return (env->Invoke("InternalCache", script)).AsClip();
}
PClip VideoProvider::ApplySubtitles(wxString _filename, PClip videosource) {
wxMutexLocker lock(AviSynthMutex);
// Insert subs
AVSValue script;
char temp[512];
strcpy(temp,_filename.mb_str(wxConvLocal));
AVSValue args[2] = { videosource, temp };
try {
script = env->Invoke("TextSub", AVSValue(args,2));
} catch (AvisynthError &err) {
throw _T("AviSynth error: ") + wxString(err.msg,wxConvLocal);
}
// Cache
return (env->Invoke("InternalCache", script)).AsClip();
}
PClip VideoProvider::ApplyDARZoom(double _zoom, double _dar, PClip videosource) {
wxMutexLocker lock(AviSynthMutex);
AVSValue script;
VideoInfo vil = videosource->GetVideoInfo();
int w = vil.height * _zoom * _dar;
int h = vil.height * _zoom;
try {
// Resize
if (!env->FunctionExists(Options.AsText(_T("Video resizer")).mb_str(wxConvLocal)))
throw AvisynthError("Selected resizer doesn't exist");
AVSValue args[3] = { videosource, w, h };
script = env->Invoke(Options.AsText(_T("Video resizer")).mb_str(wxConvLocal), AVSValue(args,3));
} catch (AvisynthError &err) {
throw _T("AviSynth error: ") + wxString(err.msg,wxConvLocal);
}
vi = script.AsClip()->GetVideoInfo();
return (env->Invoke("InternalCache",script)).AsClip();
}
wxBitmap VideoProvider::GetFrame(int n, bool force) {
if (n != last_fnum || force) {
wxMutexLocker lock(AviSynthMutex);
PVideoFrame frame = ResizedVideo->GetFrame(n,env);
//will fail if not rgb32
if (!data)
data = new unsigned char[vi.width*vi.height*vi.BitsPerPixel()/8];
unsigned char* dst = data+(vi.width*(vi.height-1)*vi.BitsPerPixel()/8);
int rs = vi.RowSize();
const unsigned char* src = frame->GetReadPtr();
int srcpitch = frame->GetPitch();
for (int i = 0; i < vi.height; i++) {
memcpy(dst,src,rs);
src+=srcpitch;
dst-=rs;
}
last_frame = wxBitmap((const char*)data, vi.width, vi.height, vi.BitsPerPixel());
last_fnum = n;
}
return wxBitmap(last_frame);
}
void VideoProvider::LoadVSFilter() {
// Loading an avisynth plugin multiple times does almost nothing
wxFileName vsfilterPath(AegisubApp::folderName + _T("vsfilter.dll"));
if (vsfilterPath.FileExists())
env->Invoke("LoadPlugin",env->SaveString(vsfilterPath.GetFullPath().mb_str(wxConvLocal)));
else {
wxRegKey reg(_T("HKEY_CLASSES_ROOT\\CLSID\\{9852A670-F845-491B-9BE6-EBD841B8A613}\\InprocServer32"));
if (reg.Exists()) {
wxString fn;
reg.QueryValue(_T(""),fn);
vsfilterPath = fn;
if (vsfilterPath.FileExists()) {
env->Invoke("LoadPlugin",env->SaveString(vsfilterPath.GetFullPath().mb_str(wxConvLocal)));
return;
}
vsfilterPath = _T("vsfilter.dll");
} else if (vsfilterPath.FileExists())
env->Invoke("LoadPlugin",env->SaveString(vsfilterPath.GetFullPath().mb_str(wxConvLocal)));
else if (!env->FunctionExists("TextSub"))
throw _T("Couldn't locate VSFilter");
}
}

100
core/video_provider.h Normal file
View File

@ -0,0 +1,100 @@
// Copyright (c) 2006, Fredrik Mellbin
// 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
//
#ifndef VIDEO_PROVIDER_H
#define VIDEO_PROVIDER_H
#include "avisynth_wrap.h"
/*class GetFrameVPThread: public wxThread {
private:
int getting_n;
int current_n;
PClip video;
wxThread::ExitCode Entry();
public:
void GetFrame(int n);
GetFrameVPThread(PClip clip);
};*/
class VideoProvider: public AviSynthWrapper {
private:
VideoInfo vi;
wxString subfilename;
int last_fnum;
unsigned char* data;
wxBitmap last_frame;
double dar;
double zoom;
PClip RGB32Video;
PClip SubtitledVideo;
PClip ResizedVideo;
PClip OpenVideo(wxString _filename, bool &usedDirectshow, bool mpeg2dec3_priority = true);
PClip ApplySubtitles(wxString _filename, PClip videosource);
PClip ApplyDARZoom(double _zoom, double _dar, PClip videosource);
wxBitmap GetFrame(int n, bool force);
void LoadVSFilter();
public:
VideoProvider(wxString _filename, wxString _subfilename, double _zoom, bool &usedDirectshow, bool mpeg2dec3_priority = true);
~VideoProvider();
void RefreshSubtitles();
void SetDAR(double _dar);
void SetZoom(double _zoom);
wxBitmap GetFrame(int n) { return GetFrame(n,false); };
// properties
int GetPosition() { return last_fnum; };
int GetFrameCount() { return vi.num_frames; };
double GetFPS() { return (double)vi.fps_numerator/(double)vi.fps_denominator; };
int GetWidth() { return vi.width; };
int GetHeight() { return vi.height; };
int GetSourceWidth() { return RGB32Video->GetVideoInfo().width; };
int GetSourceHeight() { return RGB32Video->GetVideoInfo().height; };
};
#endif