Merge branches 'info', 'bugfixes', 'workarounds', 'fixes', 'folding', 'bestsource', 'avisynth' and 'vapoursynth' into feature

This commit is contained in:
arch1t3cht 2022-09-27 23:37:18 +02:00
64 changed files with 174 additions and 47 deletions

View File

@ -2,7 +2,7 @@
Go [here](#branchfeature-list) for the new features.
### Don't we have enough Aegisub forks already??
We absolutely do, and I'm aware that adding another one [doesn't sound like a good idea on paper](https://xkcd.com/927/). However,
We absolutely do, and I'm aware that adding another one [doesn't sound like](https://xkcd.com/927/) a [good idea on paper](https://cdn.discordapp.com/attachments/425357202963038208/1007103606421459004/unknown.png). However,
- None of the existing forks are completely satisfying at the moment:
- [wangqr's fork](https://github.com/wangqr/Aegisub) is actively maintained, but focussing more on stability. It's missing most of the modern features.
@ -28,10 +28,11 @@ This list is for navigating the repository. Go to the [release page](https://git
- [`bestsource`](https://github.com/arch1t3cht/Aegisub/tree/bestsource): Add BestSource audio and video source. This source is slower than others by multiple orders of magnitude, but in exchange it can guarantee exact seeking.
- [`vapoursynth`](https://github.com/arch1t3cht/Aegisub/tree/vapoursynth): Add Vapoursynth audio and video source
- [`bugfixes`](https://github.com/arch1t3cht/Aegisub/tree/bugfixes): Various fixes necessary for compilation. Most branches are based on this.
- [`workarounds`](https://github.com/arch1t3cht/Aegisub/tree/workarounds): Same as `bugfixes`, but these are hacky fixes that probably shouldn't be pulled without more work.
- [`fixes`](https://github.com/arch1t3cht/Aegisub/tree/fixes): Miscellaneous bugfixes
- [`misc`](https://github.com/arch1t3cht/Aegisub/tree/misc): Other miscellaneous additions
- [`misc_dc`](https://github.com/arch1t3cht/Aegisub/tree/misc_dc): Miscellaneous changes taken from AegisubDC
- [`xa-ds2`](https://github.com/arch1t3cht/Aegisub/tree/xa-ds2): Add XAudio2 backend and allow stereo playback for some other backends, by wangqr and Shinon.
- [`xa2-ds`](https://github.com/arch1t3cht/Aegisub/tree/xa2-ds): Add XAudio2 backend and allow stereo playback for some other backends, by wangqr and Shinon.
- [`stereo`](https://github.com/arch1t3cht/Aegisub/tree/stereo): Add multi-channel support for the other audio backends where possible.
- [`video_panning_feature`](https://github.com/arch1t3cht/Aegisub/tree/video_panning_feature): Merge [moex3's video zoom and panning](https://github.com/TypesettingTools/Aegisub/pull/150), with an OSX fix and more options to control zoom behavior
- [`spectrum-frequency-mapping`](https://github.com/arch1t3cht/Aegisub/tree/spectrum-frequency-mapping): Merge EleonoreMizo's [spectrum display improvements](https://github.com/TypesettingTools/Aegisub/pull/94), and also make Shift+Scroll vertically zoom the audio display
@ -43,13 +44,10 @@ If it wasn't introduced by my fork, I can still take a look, but I can't promise
You can find me for support on various servers, including the cave and the TSTools server linked below.
#### Building fails with a "CMake sandbox violation"
This is an upstream bug in meson. For now, you need to downgrade meson using `pip install meson==0.62.2`.
#### Aegisub on Linux doesn't recognize my GTK theme
This is probably because you're building with wxgtk2. Building with wxgtk3 fixes this, but causes some problems of its own (notably the broken color picker, occasional crashes when opening file dialogs from automation scripts, and general layouting issues).
The exact way of switching depends on your Linux distribution, but essentially you need to ensure that `wx-config` or the next best variant of it points to wxgtk3. If it points to wxgtk2 by default and deinstalling wxgtk2 isn't an option, you can also temporarily move it out of the path. Then, fully reconfigure meson using `meson configure --clearcache` and `meson setup --reconfigure`.
The exact way of switching depends on your Linux distribution, but essentially you need to ensure that `wx-config` or the next best variant of it points to wxgtk3. If it points to wxgtk2 by default and deinstalling wxgtk2 isn't an option, you can also temporarily move it out of the path or use a `native-file` in your meson project. Then, fully reconfigure meson using `meson configure --clearcache` and `meson setup --reconfigure`.
#### I get errors like "Option not found" after merging one of these branches
The changes to `default_config.json` or similar files weren't detected by meson due to missing regen dependencies. You can either merge the `bugfixes` branch or rebuild from scratch.

View File

@ -360,7 +360,7 @@ if not deps.contains(luajit)
else
luajit_inc = include_directories(luajit.get_variable('includedir'))
endif
subdir('subprojects/luabins/src')
subdir('vendor/luabins/src')
dep_gl = dependency('gl', required: false)
if not dep_gl.found()

View File

@ -53,9 +53,9 @@ Write-Output 'Copying - translations'
Copy-New-Items "$InstallerDir\share\locale\*" "$PortableOutputDir\locale" -Recurse
Write-Output 'Copying - codecs'
Write-Output 'Copying - codecs\Avisynth'
Copy-New-Item $InstallerDepsDir\AvisynthPlus64\x86-64\DevIL.dll $PortableOutputDir
Copy-New-Item $InstallerDepsDir\AvisynthPlus64\x86-64\AviSynth.dll $PortableOutputDir
Copy-New-Item $InstallerDepsDir\AvisynthPlus64\x86-64\plugins\DirectShowSource.dll $PortableOutputDir
Copy-New-Item $InstallerDepsDir\AvisynthPlus64\x64\system\DevIL.dll $PortableOutputDir
Copy-New-Item $InstallerDepsDir\AvisynthPlus64\x64\AviSynth.dll $PortableOutputDir
Copy-New-Item $InstallerDepsDir\AvisynthPlus64\x64\plugins\DirectShowSource.dll $PortableOutputDir
Write-Output 'Copying - codecs\VSFilter'
Copy-New-Item $InstallerDepsDir\VSFilter\x64\VSFilter.dll $PortableOutputDir\csri
Write-Output 'Copying - runtimes\MS-CRT'

View File

@ -64,8 +64,6 @@ public:
};
AvisynthAudioProvider::AvisynthAudioProvider(agi::fs::path const& filename) try {
agi::acs::CheckFileRead(filename);
std::lock_guard<std::mutex> lock(avs_wrapper.GetMutex());
try {
@ -150,6 +148,7 @@ void AvisynthAudioProvider::FillBuffer(void *buf, int64_t start, int64_t count)
}
std::unique_ptr<agi::AudioProvider> CreateAvisynthAudioProvider(agi::fs::path const& file, agi::BackgroundRunner *) {
agi::acs::CheckFileRead(file);
return agi::make_unique<AvisynthAudioProvider>(file);
}
#endif

View File

@ -28,13 +28,13 @@
#include <libaegisub/access.h>
#include <libaegisub/format.h>
#include <libaegisub/fs.h>
#include <libaegisub/path.h>
#include <libaegisub/make_unique.h>
#include <mutex>
#include "vapoursynth_wrap.h"
#include "vapoursynth_common.h"
#include "VSScript4.h"
namespace {
@ -54,7 +54,6 @@ public:
};
VapoursynthAudioProvider::VapoursynthAudioProvider(agi::fs::path const& filename) try {
agi::acs::CheckFileRead(filename);
std::lock_guard<std::mutex> lock(vs.GetMutex());
script = vs.GetScriptAPI()->createScript(nullptr);
@ -62,7 +61,7 @@ VapoursynthAudioProvider::VapoursynthAudioProvider(agi::fs::path const& filename
throw VapoursynthError("Error creating script API");
}
vs.GetScriptAPI()->evalSetWorkingDir(script, 1);
if (vs.GetScriptAPI()->evaluateFile(script, filename.string().c_str())) {
if (OpenScriptOrVideo(vs.GetScriptAPI(), script, filename, OPT_GET("Provider/Audio/VapourSynth/Default Script")->GetString())) {
std::string msg = agi::format("Error executing VapourSynth script: %s", vs.GetScriptAPI()->getError(script));
vs.GetScriptAPI()->freeScript(script);
throw VapoursynthError(msg);
@ -162,6 +161,7 @@ VapoursynthAudioProvider::~VapoursynthAudioProvider() {
}
std::unique_ptr<agi::AudioProvider> CreateVapoursynthAudioProvider(agi::fs::path const& file, agi::BackgroundRunner *) {
agi::acs::CheckFileRead(file);
return agi::make_unique<VapoursynthAudioProvider>(file);
}
#endif

View File

@ -783,27 +783,33 @@ namespace {
{
bool failed = false;
BackgroundScriptRunner bsr(parent, title);
bsr.Run([&](ProgressSink *ps) {
LuaProgressSink lps(L, ps, can_open_config);
try {
bsr.Run([&](ProgressSink *ps) {
LuaProgressSink lps(L, ps, can_open_config);
// Insert our error handler under the function to call
lua_pushcclosure(L, add_stack_trace, 0);
lua_insert(L, -nargs - 2);
// Insert our error handler under the function to call
lua_pushcclosure(L, add_stack_trace, 0);
lua_insert(L, -nargs - 2);
if (lua_pcall(L, nargs, nresults, -nargs - 2)) {
if (!lua_isnil(L, -1)) {
// if the call failed, log the error here
ps->Log("\n\nLua reported a runtime error:\n");
ps->Log(get_string_or_default(L, -1));
if (lua_pcall(L, nargs, nresults, -nargs - 2)) {
if (!lua_isnil(L, -1)) {
// if the call failed, log the error here
ps->Log("\n\nLua reported a runtime error:\n");
ps->Log(get_string_or_default(L, -1));
}
lua_pop(L, 2);
failed = true;
}
lua_pop(L, 2);
failed = true;
}
else
lua_remove(L, -nresults - 1);
else
lua_remove(L, -nresults - 1);
lua_gc(L, LUA_GCCOLLECT, 0);
});
lua_gc(L, LUA_GCCOLLECT, 0);
});
} catch (agi::UserCancelException const&) {
if (!failed)
lua_pop(L, 2);
throw;
}
if (failed)
throw agi::UserCancelException("Script threw an error");
}

View File

@ -483,13 +483,13 @@ void BaseGrid::OnMouseEvent(wxMouseEvent &event) {
else {
// Only scroll if the mouse has moved to a different row to avoid
// scrolling on sloppy clicks
if (row != extendRow) {
if (VisRowToRow(row) != extendRow) {
if (row <= yPos)
ScrollTo(yPos - 3);
// When dragging down we give a 3 row margin to make it easier
// to see what's going on, but we don't want to scroll down if
// the user clicks on the bottom row and drags up
else if (row > yPos + h / lineHeight - (row > extendRow ? 3 : 1))
else if (row > yPos + h / lineHeight - (VisRowToRow(row) > extendRow ? 3 : 1))
ScrollTo(yPos + 3);
}
}
@ -512,7 +512,7 @@ void BaseGrid::OnMouseEvent(wxMouseEvent &event) {
int old_y_pos = yPos;
context->selectionController->SetActiveLine(dlg);
ScrollTo(old_y_pos);
extendRow = row;
extendRow = VisRowToRow(row);
auto const& selection = context->selectionController->GetSelectedSet();
@ -541,7 +541,7 @@ void BaseGrid::OnMouseEvent(wxMouseEvent &event) {
// Block select
if ((click && shift && !alt) || holding) {
extendRow = old_extend;
int i1 = row;
int i1 = VisRowToRow(row);
int i2 = extendRow;
if (i1 > i2)
@ -550,7 +550,7 @@ void BaseGrid::OnMouseEvent(wxMouseEvent &event) {
// Toggle each
Selection newsel;
if (ctrl) newsel = selection;
for (int i = VisRowToRow(i1); i <= VisRowToRow(i2); i++)
for (int i = i1; i <= i2; i++)
newsel.insert(GetDialogue(i));
context->selectionController->SetSelectedSet(std::move(newsel));
return;
@ -739,14 +739,14 @@ void BaseGrid::OnKeyDown(wxKeyEvent &event) {
if (shift && !ctrl && !alt) {
extendRow = old_extend;
// Set range
int begin = next;
int begin = VisRowToRow(next);
int end = extendRow;
if (end < begin)
std::swap(begin, end);
// Select range
Selection newsel;
for (int i = VisRowToRow(begin); i <= VisRowToRow(end); i++)
for (int i = begin; i <= end; i++)
newsel.insert(GetDialogue(i));
context->selectionController->SetSelectedSet(std::move(newsel));

View File

@ -340,6 +340,9 @@
"BestSource": {
"Max Cache Size" : 100,
"Aegisub Cache" : true
},
"VapourSynth" : {
"Default Script" : "import vapoursynth as vs\nvs.core.bas.Source(source=filename).set_output()"
}
},
"Avisynth" : {
@ -365,6 +368,9 @@
"Max Cache Size" : 1024,
"Threads" : 0,
"Seek Preroll" : 12
},
"VapourSynth" : {
"Default Script" : "import vapoursynth as vs\nvs.core.lsmas.LWLibavSource(source=filename).set_output()"
}
}
},

View File

@ -340,6 +340,9 @@
"BestSource": {
"Max Cache Size" : 100,
"Aegisub Cache" : true
},
"VapourSynth" : {
"Default Script" : "import vapoursynth as vs\nvs.core.bas.Source(source=filename).set_output()"
}
},
"Avisynth" : {
@ -365,6 +368,9 @@
"Max Cache Size" : 1024,
"Threads" : 0,
"Seek Preroll" : 12
},
"VapourSynth" : {
"Default Script" : "import vapoursynth as vs\nvs.core.lsmas.LWLibavSource(source=filename).set_output()"
}
}
},

View File

@ -215,6 +215,7 @@ elif host_machine.system() == 'windows'
depends: version_h,
include_directories: [res_inc, version_inc, wx_inc])
endif
aegisub_src += windows.compile_resources('res/strings.rc')
endif
if conf.has('WITH_FONTCONFIG')
@ -241,6 +242,7 @@ opt_src = [
'video_provider_bestsource.cpp',
'bestsource_common.cpp']],
['VapourSynth', ['vapoursynth_wrap.cpp',
'vapoursynth_common.cpp',
'audio_provider_vs.cpp',
'video_provider_vs.cpp']],

View File

@ -486,6 +486,32 @@ void Advanced_Video(wxTreebook *book, Preferences *parent) {
p->SetSizerAndFit(p->sizer);
}
void VapourSynth(wxTreebook *book, Preferences *parent) {
#ifdef WITH_VAPOURSYNTH
auto p = new OptionPage(book, parent, _("VapourSynth"), OptionPage::PAGE_SUB);
auto video = p->PageSizer(_("Default Video Script"));
auto vhint = new wxStaticText(p, wxID_ANY, _("This script will be executed to load video files that aren't\nVapourSynth scripts (i.e. end in .py or .vpy).\nThe filename variable stores the path to the file."));
p->sizer->Fit(p);
vhint->Wrap(400);
video->Add(vhint, 0, wxALL, 5);
video->AddSpacer(16);
p->OptionAddMultiline(video, "Provider/Video/VapourSynth/Default Script");
auto audio = p->PageSizer(_("Default Audio Script"));
auto ahint = new wxStaticText(p, wxID_ANY, _("This script will be executed to load audio files that aren't\nVapourSynth scripts (i.e. end in .py or .vpy).\nThe filename variable stores the path to the file."));
p->sizer->Fit(p);
ahint->Wrap(400);
audio->Add(ahint, 0, wxALL, 5);
audio->AddSpacer(16);
p->OptionAddMultiline(audio, "Provider/Audio/VapourSynth/Default Script");
p->SetSizerAndFit(p->sizer);
#endif
}
/// wxDataViewIconTextRenderer with command name autocompletion
class CommandRenderer final : public wxDataViewCustomRenderer {
wxArrayString autocomplete;
@ -741,6 +767,7 @@ Preferences::Preferences(wxWindow *parent): wxDialog(parent, -1, _("Preferences"
Advanced(book, this);
Advanced_Audio(book, this);
Advanced_Video(book, this);
VapourSynth(book, this);
book->Fit();

View File

@ -156,6 +156,20 @@ wxControl *OptionPage::OptionAdd(wxFlexGridSizer *flex, const wxString &name, co
}
}
wxControl *OptionPage::OptionAddMultiline(wxSizer *sizer, const char *opt_name) {
parent->AddChangeableOption(opt_name);
const auto opt = OPT_GET(opt_name);
if (opt->GetType() != agi::OptionType::String) {
throw agi::InternalError("Unsupported type for multiline option");
}
auto text = new wxTextCtrl(this, -1, to_wx(opt->GetString()), wxDefaultPosition, wxSize(-1, 200), wxTE_MULTILINE);
text->Bind(wxEVT_TEXT, StringUpdater(opt_name, parent));
sizer->Add(text, wxSizerFlags().Expand());
return text;
}
void OptionPage::OptionChoice(wxFlexGridSizer *flex, const wxString &name, const wxArrayString &choices, const char *opt_name) {
parent->AddChangeableOption(opt_name);
const auto opt = OPT_GET(opt_name);

View File

@ -43,6 +43,7 @@ public:
void CellSkip(wxFlexGridSizer *flex);
wxControl *OptionAdd(wxFlexGridSizer *flex, const wxString &name, const char *opt_name, double min=0, double max=INT_MAX, double inc=1);
wxControl *OptionAddMultiline(wxSizer *flex, const char *opt_name);
void OptionChoice(wxFlexGridSizer *flex, const wxString &name, const wxArrayString &choices, const char *opt_name);
void OptionBrowse(wxFlexGridSizer *flex, const wxString &name, const char *opt_name, wxControl *enabler = nullptr, bool do_enable = false);
void OptionFont(wxSizer *sizer, std::string opt_prefix);

View File

@ -0,0 +1,37 @@
// Copyright (c) 2022, arch1t3cht <arch1t3cht@gmail.com>
//
// Permission to use, copy, modify, and distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
//
// Aegisub Project http://www.aegisub.org/
#ifdef WITH_VAPOURSYNTH
#include "vapoursynth_common.h"
#include "options.h"
#include <libaegisub/fs.h>
#include <boost/algorithm/string/replace.hpp>
int OpenScriptOrVideo(const VSSCRIPTAPI *api, VSScript *script, agi::fs::path const& filename, std::string default_script) {
if (agi::fs::HasExtension(filename, "py") || agi::fs::HasExtension(filename, "vpy")) {
return api->evaluateFile(script, filename.string().c_str());
} else {
std::string fname = filename.string();
boost::replace_all(fname, "\\", "\\\\");
boost::replace_all(fname, "'", "\\'");
std::string vscript = "filename = '" + fname + "'\n" + default_script;
return api->evaluateBuffer(script, vscript.c_str(), "aegisub");
}
}
#endif // WITH_VAPOURSYNTH

24
src/vapoursynth_common.h Normal file
View File

@ -0,0 +1,24 @@
// Copyright (c) 2022, arch1t3cht <arch1t3cht@gmail.com>
//
// Permission to use, copy, modify, and distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
//
// Aegisub Project http://www.aegisub.org/
#ifdef WITH_VAPOURSYNTH
#include "VSScript4.h"
#include <libaegisub/fs_fwd.h>
int OpenScriptOrVideo(const VSSCRIPTAPI *api, VSScript *script, agi::fs::path const& filename, std::string default_script);
#endif // WITH_VAPOURSYNTH

View File

@ -94,8 +94,6 @@ public:
};
AvisynthVideoProvider::AvisynthVideoProvider(agi::fs::path const& filename, std::string const& colormatrix) try {
agi::acs::CheckFileRead(filename);
std::lock_guard<std::mutex> lock(avs.GetMutex());
#ifdef _WIN32
@ -326,6 +324,7 @@ void AvisynthVideoProvider::GetFrame(int n, VideoFrame &out) {
namespace agi { class BackgroundRunner; }
std::unique_ptr<VideoProvider> CreateAvisynthVideoProvider(agi::fs::path const& path, std::string const& colormatrix, agi::BackgroundRunner *) {
agi::acs::CheckFileRead(path);
return agi::make_unique<AvisynthVideoProvider>(path, colormatrix);
}
#endif // HAVE_AVISYNTH

View File

@ -147,7 +147,7 @@ BSVideoProvider::BSVideoProvider(agi::fs::path const& filename, std::string cons
Keyframes.push_back(n);
}
TimecodesVector.push_back((int) frame->GetAVFrame()->pts);
TimecodesVector.push_back(frame->Pts * properties.TimeBase.Den / properties.TimeBase.Num);
ps->SetProgress(n, properties.NumFrames);
}
@ -182,6 +182,14 @@ void BSVideoProvider::GetFrame(int n, VideoFrame &out) {
throw VideoDecodeError("Couldn't convert frame!");
}
int range = frame->color_range == AVCOL_RANGE_JPEG;
const int *coefficients = sws_getCoefficients(frame->colorspace == AVCOL_SPC_UNSPECIFIED ? AVCOL_SPC_BT709 : frame->colorspace);
sws_setColorspaceDetails(context,
coefficients, range,
coefficients, range,
0, 1 << 16, 1 << 16);
out.data.resize(frame->width * frame->height * 4);
uint8_t *data[1] = {&out.data[0]};
int stride[1] = {frame->width * 4};

View File

@ -22,13 +22,13 @@
#include <libaegisub/access.h>
#include <libaegisub/format.h>
#include <libaegisub/fs.h>
#include <libaegisub/path.h>
#include <libaegisub/make_unique.h>
#include <mutex>
#include "vapoursynth_wrap.h"
#include "vapoursynth_common.h"
#include "VSScript4.h"
#include "VSHelper4.h"
#include "VSConstants4.h"
@ -105,7 +105,6 @@ void VapoursynthVideoProvider::SetResizeArg(VSMap *args, const VSMap *props, con
}
VapoursynthVideoProvider::VapoursynthVideoProvider(agi::fs::path const& filename, std::string const& colormatrix) try {
agi::acs::CheckFileRead(filename);
std::lock_guard<std::mutex> lock(vs.GetMutex());
script = vs.GetScriptAPI()->createScript(nullptr);
@ -113,7 +112,7 @@ VapoursynthVideoProvider::VapoursynthVideoProvider(agi::fs::path const& filename
throw VapoursynthError("Error creating script API");
}
vs.GetScriptAPI()->evalSetWorkingDir(script, 1);
if (vs.GetScriptAPI()->evaluateFile(script, filename.string().c_str())) {
if (OpenScriptOrVideo(vs.GetScriptAPI(), script, filename, OPT_GET("Provider/Video/VapourSynth/Default Script")->GetString())) {
std::string msg = agi::format("Error executing VapourSynth script: %s", vs.GetScriptAPI()->getError(script));
vs.GetScriptAPI()->freeScript(script);
throw VapoursynthError(msg);
@ -277,6 +276,7 @@ VapoursynthVideoProvider::~VapoursynthVideoProvider() {
namespace agi { class BackgroundRunner; }
std::unique_ptr<VideoProvider> CreateVapoursynthVideoProvider(agi::fs::path const& path, std::string const& colormatrix, agi::BackgroundRunner *) {
agi::acs::CheckFileRead(path);
return agi::make_unique<VapoursynthVideoProvider>(path, colormatrix);
}
#endif // HAVE_VAPOURSYNTH
#endif // WITH_VAPOURSYNTH

View File

@ -71,7 +71,7 @@ if (!(Test-Path ffi-experiments)) {
# VC++ redistributable
if (!(Test-Path VC_redist)) {
$redistDir = New-Item -ItemType Directory VC_redist
Invoke-WebRequest https://aka.ms/vs/16/release/VC_redist.x64.exe -OutFile "$redistDir\VC_redist.x64.exe" -UseBasicParsing
Invoke-WebRequest https://aka.ms/vs/17/release/VC_redist.x64.exe -OutFile "$redistDir\VC_redist.x64.exe" -UseBasicParsing
}
# TODO dictionaries