From 58acde59755dcb1201beb9377d894a287d95f86e Mon Sep 17 00:00:00 2001 From: tomcup Date: Thu, 27 Jul 2023 14:35:06 +0800 Subject: [PATCH] Add test and add SCProcess function --- CMakeLists.txt | 8 ++- MiscTomcup.cpp | 121 +++++++++++++++++++++++++++++++++++++++---- tests/CMakeLists.txt | 6 +++ tests/test.cpp | 35 +++++++++++++ 4 files changed, 159 insertions(+), 11 deletions(-) create mode 100644 tests/CMakeLists.txt create mode 100644 tests/test.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 1227ed3..e04cfcd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,7 +8,13 @@ add_library(${PROJECT_NAME} SHARED "MiscTomcup.cpp") add_subdirectory(csv-parser) -target_include_directories(${PROJECT_NAME} PRIVATE ${VapourSynthSDK}/include csv-parser/include) +target_include_directories(${PROJECT_NAME} PRIVATE ${VapourSynthSDK}/include ${PROJECT_SOURCE_DIR}/csv-parser/include) target_link_libraries(${PROJECT_NAME} csv) +option(BUILD_TEST "Whether or not to build the tests" ON) +if (${BUILD_TEST}) + message(STATUS "Building tests") + add_subdirectory(tests) +endif () + set_property(TARGET ${PROJECT_NAME} PROPERTY CXX_STANDARD 20) diff --git a/MiscTomcup.cpp b/MiscTomcup.cpp index 60cf556..b7423cb 100644 --- a/MiscTomcup.cpp +++ b/MiscTomcup.cpp @@ -1,10 +1,17 @@ #include "VapourSynth4.h" #include #include +#include #include #include #include "csv.hpp" +namespace tomcup::plugin::sc { + static std::string identifier{"com.tomcup.misc"}; +} + +namespace tcp = tomcup::plugin; + struct SCDetectData { private: const VSAPI* vsapi; @@ -18,11 +25,36 @@ struct SCDetectData { ~SCDetectData() { vsapi->freeNode(node); + scdata.clear(); + } +}; + +struct SCProcessData { +private: + const VSAPI* vsapi; +public: + VSNode* originNode{ nullptr }; + VSNode* processedNode{ nullptr }; + std::map scdata; + + explicit SCProcessData(const VSAPI* vsapi) noexcept : vsapi(vsapi) { + + } + + ~SCProcessData() { + vsapi->freeNode(originNode); + vsapi->freeNode(processedNode); + scdata.clear(); } + }; +template +static void VS_CC filterFree(void* instanceData, VSCore* core, const VSAPI* vsapi) { + delete reinterpret_cast(instanceData); +} -static const VSFrame* VS_CC filterGetFrame(int n, int activationReason, void* instanceData, void** frameData, VSFrameContext* frameCtx, VSCore* core, const VSAPI* vsapi) { +static const VSFrame* VS_CC SCDetectGetFrame(int n, int activationReason, void* instanceData, void** frameData, VSFrameContext* frameCtx, VSCore* core, const VSAPI* vsapi) { SCDetectData* d = static_cast(instanceData); if (activationReason == arInitial) { @@ -43,11 +75,7 @@ static const VSFrame* VS_CC filterGetFrame(int n, int activationReason, void* in return nullptr; } -static void VS_CC filterFree(void* instanceData, VSCore* core, const VSAPI* vsapi) { - delete reinterpret_cast(instanceData); -} - -static void VS_CC filterCreate(const VSMap* in, VSMap* out, void* userData, VSCore* core, const VSAPI* vsapi) { +static void VS_CC SCDetectCreate(const VSMap* in, VSMap* out, void* userData, VSCore* core, const VSAPI* vsapi) { auto data{ std::make_unique(vsapi) }; data->node = vsapi->mapGetNode(in, "clip", 0, nullptr); @@ -55,7 +83,7 @@ static void VS_CC filterCreate(const VSMap* in, VSMap* out, void* userData, VSCo const std::string_view scfile{vsapi->mapGetData(in, "scfile", 0, nullptr)}; std::filesystem::path scfilepath{scfile}; if (scfilepath.is_relative()) - scfilepath = std::filesystem::path(vsapi->getPluginPath(vsapi->getPluginByID("com.tomcup.SCDetect", core))).remove_filename().append(scfile); + scfilepath = std::filesystem::path(vsapi->getPluginPath(vsapi->getPluginByID(tcp::sc::identifier.c_str(), core))).remove_filename().append(scfile); const std::filesystem::directory_entry scfileEntry{scfilepath}; const VSVideoInfo* vi = vsapi->getVideoInfo(data->node); @@ -73,10 +101,83 @@ static void VS_CC filterCreate(const VSMap* in, VSMap* out, void* userData, VSCo } VSFilterDependency deps[] = { {data->node, rpGeneral} }; - vsapi->createVideoFilter(out, "SCDetect", vi, filterGetFrame, filterFree, fmParallel, deps, 1, data.release(), core); + vsapi->createVideoFilter(out, "SCDetect", vi, SCDetectGetFrame, filterFree, fmParallel, deps, 1, data.release(), core); +} + +static const VSFrame* VS_CC SCProcessGetFrame(int n, int activationReason, void* instanceData, void** frameData, VSFrameContext* frameCtx, VSCore* core, const VSAPI* vsapi) { + SCProcessData* d = static_cast(instanceData); + + auto it{ d->scdata.find(n) }; + if (it == d->scdata.end()) { + if (activationReason == arInitial) { + vsapi->requestFrameFilter(n, d->processedNode, frameCtx); + } + else if (activationReason == arAllFramesReady) { + return vsapi->getFrameFilter(n, d->processedNode, frameCtx); + } + } + else { + if (activationReason == arInitial) { + vsapi->requestFrameFilter(it->second, d->originNode, frameCtx); + } + else if (activationReason == arAllFramesReady) { + return vsapi->getFrameFilter(it->second, d->originNode, frameCtx); + } + } + + return nullptr; +} + +static void VS_CC SCProcessCreate(const VSMap* in, VSMap* out, void* userData, VSCore* core, const VSAPI* vsapi) { + auto data{ std::make_unique(vsapi) }; + + data->originNode = vsapi->mapGetNode(in, "originClip", 0, nullptr); + data->processedNode = vsapi->mapGetNode(in, "processedClip", 0, nullptr); + + const VSVideoInfo* vi{ vsapi->getVideoInfo(data->processedNode) }; + + const std::string_view scfile{vsapi->mapGetData(in, "originscfile", 0, nullptr)}; + std::filesystem::path scfilepath{scfile}; + if (scfilepath.is_relative()) + scfilepath = std::filesystem::path(vsapi->getPluginPath(vsapi->getPluginByID(tcp::sc::identifier.c_str(), core))).remove_filename().append(scfile); + const std::filesystem::directory_entry scfileEntry{scfilepath}; + + try { + const VSVideoInfo* _vi{ vsapi->getVideoInfo(data->originNode) }; + if (_vi->height != vi->height && _vi->width != vi->width) { + throw std::runtime_error("height or width is different between origin and processed video!"); + }; + + if (!scfileEntry.exists()) + throw std::runtime_error(std::string("target scfile not exists: ") + scfilepath.generic_string()); + csv::CSVReader reader(scfilepath.generic_string()); + for (csv::CSVRow& row : reader) { + int target{ row["End Frame"].get() - 1 }; + if (target % 2 == 0) { + int _target{ static_cast(target * 2.5f) }; + data->scdata.emplace(_target, target); + data->scdata.emplace(_target + 1, target); + data->scdata.emplace(_target + 2, target + 1); + } + else { + int _target{ static_cast((target + 1) * 2.5f) }; + data->scdata.emplace(_target, target + 1); + data->scdata.emplace(_target - 1, target + 1); + data->scdata.emplace(_target - 2, target); + } + } + } + catch (const std::runtime_error& e) { + vsapi->mapSetError(out, (std::string("SCProcess: ") + e.what()).c_str()); + return; + } + + VSFilterDependency deps[] = { {data->originNode, rpGeneral}, {data->processedNode, rpGeneral} }; + vsapi->createVideoFilter(out, "SCProcess", vi, SCProcessGetFrame, filterFree, fmParallel, deps, 2, data.release(), core); } VS_EXTERNAL_API(void) VapourSynthPluginInit2(VSPlugin* plugin, const VSPLUGINAPI* vspapi) { - vspapi->configPlugin("com.tomcup.SCDetect", "tomcup", "Mark the clip with PySceneDetect's output", VS_MAKE_VERSION(1, 1), VAPOURSYNTH_API_VERSION, 0, plugin); - vspapi->registerFunction("SCDetect", "clip:vnode;scfile:data", "clip:vnode;", filterCreate, 0, plugin); + vspapi->configPlugin(tcp::sc::identifier.c_str(), "tomcup", "Mark the clip with PySceneDetect's output", VS_MAKE_VERSION(1, 1), VAPOURSYNTH_API_VERSION, 0, plugin); + vspapi->registerFunction("SCDetect", "clip:vnode;scfile:data", "clip:vnode;", SCDetectCreate, nullptr, plugin); + vspapi->registerFunction("SCProcess", "originClip:vnode;processedClip:vnode;originscfile:data;", "clip:vnode;", SCProcessCreate, nullptr, plugin); } \ No newline at end of file diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 0000000..1d8c044 --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,6 @@ +add_executable(${PROJECT_NAME}Test "test.cpp") + +target_include_directories(${PROJECT_NAME}Test PRIVATE ${VapourSynthSDK}/include ${PROJECT_SOURCE_DIR}/csv-parser/include) +target_link_libraries(${PROJECT_NAME}Test csv) + +set_property(TARGET ${PROJECT_NAME}Test PROPERTY CXX_STANDARD 20) \ No newline at end of file diff --git a/tests/test.cpp b/tests/test.cpp new file mode 100644 index 0000000..253516d --- /dev/null +++ b/tests/test.cpp @@ -0,0 +1,35 @@ +#include +#include +#include +#include +#include +#include +#include "csv.hpp" + +void tlog(unsigned first, unsigned second) { + std::cout << first << '\t' << second << '\n'; +} + +int main() { + const std::string scfile{R"(C:\Users\jilig\Downloads\Suzume.2022.1080p.WEB.Rip.5.1.YTS.M.X\Suzume.2022.1080p.WEBRip.x264.AAC5.1-YTS.MX.origin-Scenes-contant.csv)"}; + std::filesystem::path scfilepath{scfile}; + const std::filesystem::directory_entry scfileEntry{scfilepath}; + + csv::CSVReader reader(scfilepath.generic_string()); + + for (csv::CSVRow& row : reader) { + unsigned target{ row["End Frame"].get() - 1 }; + if (target % 2 == 0) { + unsigned _target{ static_cast(target * 2.5f) }; + tlog(_target, target); + tlog(_target + 1, target); + tlog(_target + 2, target + 1); + } + else { + unsigned _target{ static_cast((target + 1) * 2.5f) }; + tlog(_target, target + 1); + tlog(_target - 1, target + 1); + tlog(_target - 2, target); + } + } +} \ No newline at end of file