Skip to content

Commit

Permalink
Add test and add SCProcess function
Browse files Browse the repository at this point in the history
  • Loading branch information
tomcup committed Jul 27, 2023
1 parent d9435e1 commit 58acde5
Show file tree
Hide file tree
Showing 4 changed files with 159 additions and 11 deletions.
8 changes: 7 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
121 changes: 111 additions & 10 deletions MiscTomcup.cpp
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
#include "VapourSynth4.h"
#include <memory>
#include <set>
#include <map>
#include <string>
#include <filesystem>
#include "csv.hpp"

namespace tomcup::plugin::sc {
static std::string identifier{"com.tomcup.misc"};
}

namespace tcp = tomcup::plugin;

struct SCDetectData {
private:
const VSAPI* vsapi;
Expand All @@ -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<int, int> scdata;

explicit SCProcessData(const VSAPI* vsapi) noexcept : vsapi(vsapi) {

}

~SCProcessData() {
vsapi->freeNode(originNode);
vsapi->freeNode(processedNode);
scdata.clear();
}

};

template<typename T>
static void VS_CC filterFree(void* instanceData, VSCore* core, const VSAPI* vsapi) {
delete reinterpret_cast<T*>(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<SCDetectData*>(instanceData);

if (activationReason == arInitial) {
Expand All @@ -43,19 +75,15 @@ 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<SCDetectData*>(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<SCDetectData>(vsapi) };

data->node = vsapi->mapGetNode(in, "clip", 0, nullptr);

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);
Expand All @@ -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<SCDetectData>, 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<SCProcessData*>(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<SCProcessData>(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<int>() - 1 };
if (target % 2 == 0) {
int _target{ static_cast<int>(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<int>((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<SCProcessData>, 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);
}
6 changes: 6 additions & 0 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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)
35 changes: 35 additions & 0 deletions tests/test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#include <iostream>
#include <memory>
#include <set>
#include <map>
#include <string>
#include <filesystem>
#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<unsigned>() - 1 };
if (target % 2 == 0) {
unsigned _target{ static_cast<unsigned>(target * 2.5f) };
tlog(_target, target);
tlog(_target + 1, target);
tlog(_target + 2, target + 1);
}
else {
unsigned _target{ static_cast<unsigned>((target + 1) * 2.5f) };
tlog(_target, target + 1);
tlog(_target - 1, target + 1);
tlog(_target - 2, target);
}
}
}

0 comments on commit 58acde5

Please sign in to comment.