Aegisub/core/video_provider.cpp

258 lines
7.7 KiB
C++

// 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");
}
}