diff --git a/core/dialog_properties.cpp b/core/dialog_properties.cpp index a3a9bd269..7e52d3f6d 100644 --- a/core/dialog_properties.cpp +++ b/core/dialog_properties.cpp @@ -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(); } diff --git a/core/dialog_resample.cpp b/core/dialog_resample.cpp index c87f63310..b65a4c5ae 100644 --- a/core/dialog_resample.cpp +++ b/core/dialog_resample.cpp @@ -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())); } diff --git a/core/frame_main.cpp b/core/frame_main.cpp index 16d0548d8..3288c1054 100644 --- a/core/frame_main.cpp +++ b/core/frame_main.cpp @@ -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(); } diff --git a/core/subs_grid.cpp b/core/subs_grid.cpp index f13dc75f9..e77faa0d4 100644 --- a/core/subs_grid.cpp +++ b/core/subs_grid.cpp @@ -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(); diff --git a/core/video_display.cpp b/core/video_display.cpp index 09d088bba..0f8fa1ed0 100644 --- a/core/video_display.cpp +++ b/core/video_display.cpp @@ -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,445 +111,94 @@ 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); - 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>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;yGetReadPtr(); - 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>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; +void VideoDisplay::RefreshSubtitles() { + provider->RefreshSubtitles(); + RefreshVideo(); } @@ -563,27 +207,10 @@ void VideoDisplay::GetFrameImage(int n) { 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.EndDrawing(); - } - - // Failed - else { - dc.BeginDrawing(); - dc.SetBrush(*wxBLUE_BRUSH); - dc.DrawRectangle(0,0,w,h); - dc.EndDrawing(); - } + dc.BeginDrawing(); + dc.DrawBitmap(GetFrame(frame_n),0,0); + dc.EndDrawing(); } @@ -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,13 +367,11 @@ void VideoDisplay::JumpToTime(int ms) { /////////////////// // Sets zoom level void VideoDisplay::SetZoom(double value) { - if (value != zoom) { - zoom = value; - if (loaded) { - OpenAVSSubs(); - RefreshVideo(); - GetParent()->Layout(); - } + 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(); - RefreshVideo(); - GetParent()->Layout(); - //GetParent()->Thaw(); - } + 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(); } } @@ -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,152 +523,24 @@ 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); - } - - // Draw frame - if (curFrame) { - wxMutexLocker locker(BitmapMutex); - wxClientDC dc(this); - dc.BeginDrawing(); - dc.DrawBitmap(*curFrame,0,0); - dc.EndDrawing(); - //Refresh(false); - } - - // Draw black - else { - wxClientDC dc(this); - dc.BeginDrawing(); - dc.Clear(); - dc.EndDrawing(); - } - } - - // Display - UpdatePositionDisplay(); +void VideoDisplay::RefreshVideo() { + // Draw frame + wxClientDC dc(this); + dc.BeginDrawing(); + dc.DrawBitmap(GetFrame(),0,0); + dc.EndDrawing(); } //////////////////////// // Requests a new frame -void VideoDisplay::GetFrame(int n) { - // Make sure it's loaded - if (!loaded) { - //throw _T("Video not loaded"); - return; - } - - // 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); - 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); - } +wxBitmap VideoDisplay::GetFrame(int n) { + frame_n = n; - // Return - Exit(); - return 0; + return provider->GetFrame(n); + RefreshVideo(); } @@ -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 { diff --git a/core/video_display.h b/core/video_display.h index ee85b607b..a6b7bbde8 100644 --- a/core/video_display.h +++ b/core/video_display.h @@ -42,55 +42,31 @@ // Headers #include #include -#include "avisynth_wrap.h" #include +#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); diff --git a/core/video_provider.cpp b/core/video_provider.cpp new file mode 100644 index 000000000..69c5a5c46 --- /dev/null +++ b/core/video_provider.cpp @@ -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 +#include +#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"); + } +} diff --git a/core/video_provider.h b/core/video_provider.h new file mode 100644 index 000000000..9fa5cddec --- /dev/null +++ b/core/video_provider.h @@ -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 \ No newline at end of file