vapoursynth: Use scoped_holder everywhere for RAII

This commit is contained in:
arch1t3cht 2024-10-25 17:40:57 +02:00
parent 2b855a327d
commit 2e5226421e
3 changed files with 34 additions and 86 deletions

View File

@ -30,6 +30,7 @@
#include <libaegisub/format.h>
#include <libaegisub/path.h>
#include <libaegisub/make_unique.h>
#include <libaegisub/scoped_ptr.h>
#include <mutex>
@ -40,24 +41,27 @@
namespace {
class VapourSynthAudioProvider final : public agi::AudioProvider {
VapourSynthWrapper vs;
VSScript *script = nullptr;
VSNode *node = nullptr;
agi::scoped_holder<VSScript *> script;
agi::scoped_holder<VSNode *> node;
const VSAudioInfo *vi = nullptr;
void FillBufferWithFrame(void *buf, int frame, int64_t start, int64_t count) const;
void FillBuffer(void *buf, int64_t start, int64_t count) const override;
public:
VapourSynthAudioProvider(agi::fs::path const& filename);
~VapourSynthAudioProvider();
bool NeedsCache() const override { return true; }
};
VapourSynthAudioProvider::VapourSynthAudioProvider(agi::fs::path const& filename) try {
VapourSynthAudioProvider::VapourSynthAudioProvider(agi::fs::path const& filename) try
: vs()
, script(nullptr, vs.GetScriptAPI()->freeScript)
, node(nullptr, vs.GetAPI()->freeNode) {
std::lock_guard<std::mutex> lock(vs.GetMutex());
VSCleanCache();
// createScript takes ownership of the core so no need for a scoped_holder here
VSCore *core = vs.GetAPI()->createCore(OPT_GET("Provider/VapourSynth/Autoload User Plugins")->GetBool() ? 0 : VSCoreCreationFlags::ccfDisableAutoLoading);
if (core == nullptr) {
throw VapourSynthError("Error creating core");
@ -69,17 +73,13 @@ VapourSynthAudioProvider::VapourSynthAudioProvider(agi::fs::path const& filename
vs.GetScriptAPI()->evalSetWorkingDir(script, 1);
if (OpenScriptOrVideo(vs.GetAPI(), 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);
}
node = vs.GetScriptAPI()->getOutputNode(script, 0);
if (node == nullptr) {
vs.GetScriptAPI()->freeScript(script);
throw VapourSynthError("No output node set");
}
if (vs.GetAPI()->getNodeType(node) != mtAudio) {
vs.GetAPI()->freeNode(node);
vs.GetScriptAPI()->freeScript(script);
throw VapourSynthError("Output node isn't an audio node");
}
vi = vs.GetAPI()->getAudioInfo(node);
@ -108,16 +108,14 @@ static void PackChannels(const uint8_t **Src, void *Dst, size_t Length, size_t C
void VapourSynthAudioProvider::FillBufferWithFrame(void *buf, int n, int64_t start, int64_t count) const {
char errorMsg[1024];
const VSFrame *frame = vs.GetAPI()->getFrame(n, node, errorMsg, sizeof(errorMsg));
agi::scoped_holder frame(vs.GetAPI()->getFrame(n, node, errorMsg, sizeof(errorMsg)), vs.GetAPI()->freeFrame);
if (frame == nullptr) {
throw VapourSynthError(agi::format("Error getting frame: %s", errorMsg));
}
if (vs.GetAPI()->getFrameLength(frame) < count) {
vs.GetAPI()->freeFrame(frame);
throw VapourSynthError("Audio frame too short");
}
if (vs.GetAPI()->getAudioFrameFormat(frame)->numChannels != channels || vs.GetAPI()->getAudioFrameFormat(frame)->bytesPerSample != bytes_per_sample) {
vs.GetAPI()->freeFrame(frame);
throw VapourSynthError("Audio format is not constant");
}
@ -125,7 +123,6 @@ void VapourSynthAudioProvider::FillBufferWithFrame(void *buf, int n, int64_t sta
for (int c = 0; c < channels; c++) {
planes[c] = vs.GetAPI()->getReadPtr(frame, c) + bytes_per_sample * start;
if (planes[c] == nullptr) {
vs.GetAPI()->freeFrame(frame);
throw VapourSynthError("Failed to read audio channel");
}
}
@ -138,8 +135,6 @@ void VapourSynthAudioProvider::FillBufferWithFrame(void *buf, int n, int64_t sta
PackChannels<uint32_t>(planes.data(), buf, count, channels);
else if (bytes_per_sample == 8)
PackChannels<uint64_t>(planes.data(), buf, count, channels);
vs.GetAPI()->freeFrame(frame);
}
void VapourSynthAudioProvider::FillBuffer(void *buf, int64_t start, int64_t count) const {
@ -158,14 +153,6 @@ void VapourSynthAudioProvider::FillBuffer(void *buf, int64_t start, int64_t coun
}
}
VapourSynthAudioProvider::~VapourSynthAudioProvider() {
if (node != nullptr) {
vs.GetAPI()->freeNode(node);
}
if (script != nullptr) {
vs.GetScriptAPI()->freeScript(script);
}
}
}
std::unique_ptr<agi::AudioProvider> CreateVapourSynthAudioProvider(agi::fs::path const& file, agi::BackgroundRunner *) {

View File

@ -24,6 +24,7 @@
#include <libaegisub/format.h>
#include <libaegisub/fs.h>
#include <libaegisub/path.h>
#include <libaegisub/scoped_ptr.h>
#include <libaegisub/util.h>
#include <boost/algorithm/string/replace.hpp>
@ -38,7 +39,7 @@ int OpenScriptOrVideo(const VSAPI *api, const VSSCRIPTAPI *sapi, VSScript *scrip
if (agi::fs::HasExtension(filename, "py") || agi::fs::HasExtension(filename, "vpy")) {
result = sapi->evaluateFile(script, filename.string().c_str());
} else {
VSMap *map = api->createMap();
agi::scoped_holder<VSMap *> map(api->createMap(), api->freeMap);
if (map == nullptr)
throw VapourSynthError("Failed to create VSMap for script info");
@ -58,8 +59,6 @@ int OpenScriptOrVideo(const VSAPI *api, const VSSCRIPTAPI *sapi, VSScript *scrip
if (sapi->setVariables(script, map))
throw VapourSynthError("Failed to set script info variables");
api->freeMap(map);
std::string vscript;
vscript += "import sys\n";
vscript += "sys.path.append(f'{__aegi_user}/automation/vapoursynth')\n";

View File

@ -28,6 +28,7 @@
#include <libaegisub/log.h>
#include <libaegisub/make_unique.h>
#include <libaegisub/path.h>
#include <libaegisub/scoped_ptr.h>
#include <mutex>
@ -44,9 +45,9 @@ static const char *audio_key = "__aegi_hasaudio";
namespace {
class VapourSynthVideoProvider: public VideoProvider {
VapourSynthWrapper vs;
VSScript *script = nullptr;
VSNode *source_node = nullptr;
VSNode *prepared_node = nullptr;
agi::scoped_holder<VSScript *> script;
agi::scoped_holder<VSNode *> source_node;
agi::scoped_holder<VSNode *> prepared_node;
const VSVideoInfo *vi = nullptr;
double dar = 0;
@ -57,12 +58,11 @@ class VapourSynthVideoProvider: public VideoProvider {
int video_cr = -1; // Reported or guessed color range of first frame
bool has_audio = false;
const VSFrame *GetVSFrame(VSNode *node, int n);
agi::scoped_holder<const VSFrame *, void (*)(const VSFrame *) noexcept> GetVSFrame(VSNode *node, int n);
void SetResizeArg(VSMap *args, const VSMap *props, const char *arg_name, const char *prop_name, int64_t deflt, int64_t unspecified = -1);
public:
VapourSynthVideoProvider(agi::fs::path const& filename, std::string const& colormatrix, agi::BackgroundRunner *br);
~VapourSynthVideoProvider();
void GetFrame(int n, VideoFrame &frame) override;
@ -88,19 +88,23 @@ public:
bool ShouldSetVideoProperties() const override { return colorspace != "Unknown"; }
};
VapourSynthVideoProvider::VapourSynthVideoProvider(agi::fs::path const& filename, std::string const& colormatrix, agi::BackgroundRunner *br) try { try {
VapourSynthVideoProvider::VapourSynthVideoProvider(agi::fs::path const& filename, std::string const& colormatrix, agi::BackgroundRunner *br) try
: vs()
, script(nullptr, vs.GetScriptAPI()->freeScript)
, source_node(nullptr, vs.GetAPI()->freeNode)
, prepared_node(nullptr, vs.GetAPI()->freeNode) {
std::lock_guard<std::mutex> lock(vs.GetMutex());
VSCleanCache();
int err1, err2;
// createScript takes ownership of the core so no need for a scoped_holder here
VSCore *core = vs.GetAPI()->createCore(OPT_GET("Provider/VapourSynth/Autoload User Plugins")->GetBool() ? 0 : VSCoreCreationFlags::ccfDisableAutoLoading);
if (core == nullptr) {
throw VapourSynthError("Error creating core");
}
script = vs.GetScriptAPI()->createScript(core);
if (script == nullptr) {
vs.GetAPI()->freeCore(core);
throw VapourSynthError("Error creating script API");
}
vs.GetScriptAPI()->evalSetWorkingDir(script, 1);
@ -143,7 +147,7 @@ VapourSynthVideoProvider::VapourSynthVideoProvider(agi::fs::path const& filename
fps = agi::vfr::Framerate(fpsNum, fpsDen);
// Get timecodes and/or keyframes if provided
VSMap *clipinfo = vs.GetAPI()->createMap();
agi::scoped_holder<VSMap *> clipinfo(vs.GetAPI()->createMap(), vs.GetAPI()->freeMap);
if (clipinfo == nullptr)
throw VapourSynthError("Couldn't create map");
vs.GetScriptAPI()->getVariable(script, kf_key, clipinfo);
@ -209,10 +213,9 @@ VapourSynthVideoProvider::VapourSynthVideoProvider(agi::fs::path const& filename
}
}
}
vs.GetAPI()->freeMap(clipinfo);
// Find the first frame Of the video to get some info
const VSFrame *frame = GetVSFrame(source_node, 0);
auto frame = GetVSFrame(source_node, 0);
const VSMap *props = vs.GetAPI()->getFramePropertiesRO(frame);
if (props == nullptr)
@ -235,21 +238,10 @@ VapourSynthVideoProvider::VapourSynthVideoProvider(agi::fs::path const& filename
video_cs = vs.GetAPI()->mapGetInt(props, "_Matrix", 0, &err2);
ColorMatrix::guess_colorspace(video_cs, video_cr, vi->width, vi->height);
vs.GetAPI()->freeFrame(frame);
SetColorSpace(colormatrix);
} catch (VapourSynthError const& err) { // for try inside of function. We need both here since we need to catch errors from the VapourSynthWrap constructor.
if (prepared_node != nullptr)
vs.GetAPI()->freeNode(prepared_node);
if (source_node != nullptr)
vs.GetAPI()->freeNode(source_node);
if (script != nullptr)
vs.GetScriptAPI()->freeScript(script);
throw err;
}
}
catch (VapourSynthError const& err) { // for the entire constructor
throw VideoProviderError(agi::format("VapourSynth error: %s", err.GetMessage()));
catch (VapourSynthError const& err) {
throw VideoOpenError(err.GetMessage());
}
void VapourSynthVideoProvider::SetColorSpace(std::string const& matrix) {
@ -257,11 +249,8 @@ void VapourSynthVideoProvider::SetColorSpace(std::string const& matrix) {
if (matrix == colorspace && prepared_node != nullptr) {
return;
}
if (prepared_node != nullptr) {
vs.GetAPI()->freeNode(prepared_node);
}
VSNode *intermediary = nullptr;
agi::scoped_holder<VSNode *> intermediary(vs.GetAPI()->addNodeRef(source_node), vs.GetAPI()->freeNode);
auto [force_cs, force_cr] = ColorMatrix::parse_colormatrix(matrix);
if (force_cs != AGI_CS_UNSPECIFIED && force_cr != AGI_CR_UNSPECIFIED) {
@ -270,7 +259,7 @@ void VapourSynthVideoProvider::SetColorSpace(std::string const& matrix) {
if (std == nullptr)
throw VapourSynthError("Couldn't find std plugin");
VSMap *args = vs.GetAPI()->createMap();
agi::scoped_holder<VSMap *> args(vs.GetAPI()->createMap(), vs.GetAPI()->freeMap);
if (args == nullptr)
throw VapourSynthError("Failed to create argument map");
@ -279,15 +268,12 @@ void VapourSynthVideoProvider::SetColorSpace(std::string const& matrix) {
vs.GetAPI()->mapSetInt(args, "_ColorRange", force_cr == AGI_CR_JPEG ? VSC_RANGE_FULL : VSC_RANGE_LIMITED, maAppend);
VSMap *result = vs.GetAPI()->invoke(std, "SetFrameProps", args);
vs.GetAPI()->freeMap(args);
const char *error = vs.GetAPI()->mapGetError(result);
if (error) {
vs.GetAPI()->freeMap(result);
throw VideoOpenError(agi::format("Failed set color space frame props: %s", error));
}
int err;
intermediary = vs.GetAPI()->mapGetNode(result, "clip", 0, &err);
vs.GetAPI()->freeMap(result);
if (err) {
throw VideoOpenError("Failed to get SetFrameProps output node");
}
@ -298,11 +284,11 @@ void VapourSynthVideoProvider::SetColorSpace(std::string const& matrix) {
if (resize == nullptr)
throw VapourSynthError("Couldn't find resize plugin");
VSMap *args = vs.GetAPI()->createMap();
agi::scoped_holder<VSMap *> args(vs.GetAPI()->createMap(), vs.GetAPI()->freeMap);
if (args == nullptr)
throw VapourSynthError("Failed to create argument map");
vs.GetAPI()->mapSetNode(args, "clip", intermediary == nullptr ? source_node : intermediary, maAppend);
vs.GetAPI()->mapSetNode(args, "clip", intermediary, maAppend);
vs.GetAPI()->mapSetInt(args, "format", pfRGB24, maAppend);
// Set defaults for the colorspace parameters.
@ -313,48 +299,37 @@ void VapourSynthVideoProvider::SetColorSpace(std::string const& matrix) {
vs.GetAPI()->mapSetInt(args, "chromaloc_in", VSC_CHROMA_LEFT, maAppend);
VSMap *result = vs.GetAPI()->invoke(resize, "Bicubic", args);
vs.GetAPI()->freeMap(args);
const char *error = vs.GetAPI()->mapGetError(result);
if (error) {
vs.GetAPI()->freeMap(result);
vs.GetScriptAPI()->freeScript(script);
throw VideoOpenError(agi::format("Failed to convert to RGB24: %s", error));
}
int err;
prepared_node = vs.GetAPI()->mapGetNode(result, "clip", 0, &err);
vs.GetAPI()->freeMap(result);
if (err) {
vs.GetScriptAPI()->freeScript(script);
throw VideoOpenError("Failed to get resize output node");
}
// Finally, try to get the first frame again, so if the filter does crash, it happens before loading finishes
const VSFrame *rgbframe = GetVSFrame(prepared_node, 0);
vs.GetAPI()->freeFrame(rgbframe);
if (intermediary != nullptr) {
vs.GetAPI()->freeNode(intermediary);
}
GetVSFrame(prepared_node, 0);
} else {
vs.GetAPI()->freeNode(prepared_node);
prepared_node = vs.GetAPI()->addNodeRef(source_node);
}
colorspace = matrix;
}
const VSFrame *VapourSynthVideoProvider::GetVSFrame(VSNode *node, int n) {
agi::scoped_holder<const VSFrame *, void (*)(const VSFrame *) noexcept> VapourSynthVideoProvider::GetVSFrame(VSNode *node, int n) {
char errorMsg[1024];
const VSFrame *frame = vs.GetAPI()->getFrame(n, node, errorMsg, sizeof(errorMsg));
if (frame == nullptr) {
throw VapourSynthError(agi::format("Error getting frame: %s", errorMsg));
}
return frame;
return agi::scoped_holder(frame, vs.GetAPI()->freeFrame);
}
void VapourSynthVideoProvider::GetFrame(int n, VideoFrame &out) {
std::lock_guard<std::mutex> lock(vs.GetMutex());
const VSFrame *frame = GetVSFrame(prepared_node, n);
auto frame = GetVSFrame(prepared_node, n);
const VSVideoFormat *format = vs.GetAPI()->getVideoFrameFormat(frame);
if (format->colorFamily != cfRGB || format->numPlanes != 3 || format->bitsPerSample != 8 || format->subSamplingH != 0 || format->subSamplingW != 0) {
@ -386,21 +361,8 @@ void VapourSynthVideoProvider::GetFrame(int n, VideoFrame &out) {
writePtr += out.pitch;
}
}
vs.GetAPI()->freeFrame(frame);
}
VapourSynthVideoProvider::~VapourSynthVideoProvider() {
if (prepared_node != nullptr) {
vs.GetAPI()->freeNode(prepared_node);
}
if (source_node != nullptr) {
vs.GetAPI()->freeNode(source_node);
}
if (script != nullptr) {
vs.GetScriptAPI()->freeScript(script);
}
}
}
namespace agi { class BackgroundRunner; }