Skip to content

Commit

Permalink
Input: add AviSynth+ reader
Browse files Browse the repository at this point in the history
  • Loading branch information
DJATOM committed Mar 17, 2021
1 parent 436e860 commit 3dbf9c1
Show file tree
Hide file tree
Showing 6 changed files with 424 additions and 1 deletion.
18 changes: 17 additions & 1 deletion source/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -756,8 +756,10 @@ if(ENABLE_CLI)
output/yuv.cpp output/y4m.cpp # recon
output/raw.cpp) # muxers
set(ENABLE_VAPOURSYNTH ON CACHE BOOL "Build with Vapoursynth support")
if(ENABLE_VAPOURSYNTH)
set(ENABLE_AVISYNTH ON CACHE BOOL "Build with Avisynth support")
if(ENABLE_VAPOURSYNTH OR ENABLE_AVISYNTH)
find_package(Vapoursynth)
find_package(Avisynth)
set(EXTRA_SYNTH_DEFS "")
set(EXTRA_SYNTH_INCLUDES "")
if(Vapoursynth_FOUND)
Expand All @@ -771,6 +773,14 @@ if(ENABLE_CLI)
else()
set(ENABLE_VAPOURSYNTH OFF)
endif()
if(Avisynth_FOUND)
list(APPEND InputFiles input/avs.cpp input/avs.h)
set_source_files_properties(input/avs.cpp PROPERTIES INCLUDE_DIRECTORIES "${AVS_INCLUDE_DIR}")
set(EXTRA_SYNTH_DEFS "${EXTRA_SYNTH_DEFS}HAVE_AVS;")
set(EXTRA_SYNTH_INCLUDES "${EXTRA_SYNTH_INCLUDES}${AVS_INCLUDE_DIR};")
else()
set(ENABLE_AVISYNTH OFF)
endif()
set_source_files_properties(input/input.cpp PROPERTIES INCLUDE_DIRECTORIES "${EXTRA_SYNTH_INCLUDES}")
set_source_files_properties(input/input.cpp x265cli.cpp PROPERTIES COMPILE_DEFINITIONS "${EXTRA_SYNTH_DEFS}")
if(GCC)
Expand All @@ -781,6 +791,9 @@ if(ENABLE_CLI)
set_source_files_properties(common/event.cpp PROPERTIES COMPILE_OPTIONS "-std=gnu++11")
endif()
endif()
if(Avisynth_FOUND)
set_source_files_properties(input/avs.cpp PROPERTIES COMPILE_OPTIONS "-std=gnu++11")
endif()
elseif(MSVC_VERSION GREATER_EQUAL "1900")
include(CheckCXXCompilerFlag)
CHECK_CXX_COMPILER_FLAG("/std:c++11" _cpp_latest_flag_supported)
Expand All @@ -789,6 +802,9 @@ if(ENABLE_CLI)
if(Vapoursynth_FOUND)
set_source_files_properties(input/vpy.cpp PROPERTIES COMPILE_OPTIONS "/std:c++11")
endif()
if(Avisynth_FOUND)
set_source_files_properties(input/avs.cpp PROPERTIES COMPILE_OPTIONS "/std:c++11")
endif()
endif()
endif()
endif()
Expand Down
25 changes: 25 additions & 0 deletions source/cmake/FindAvisynth.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
set(PACKAGE_CONFIG_TEXT "Avisynth include directory")

if(WIN32)
if(DEFINED ENV{AVISYNTH_SDK_PATH})
set(AVS_SDK_FOLDER $ENV{AVISYNTH_SDK_PATH})
string(REGEX REPLACE "\\\\" "/" AVS_SDK_FOLDER ${AVS_SDK_FOLDER})
set(AVS_INCLUDE_DIR "${AVS_SDK_FOLDER}/include" CACHE PATH "${PACKAGE_CONFIG_TEXT}")
endif()
else()
find_path(AVS_INCLUDE_PREFIX NAMES avisynth PATHS usr PATH_SUFFIXES include)
if(AVS_INCLUDE_PREFIX)
set(AVS_INCLUDE_DIR "${AVS_INCLUDE_PREFIX}/avisynth" CACHE PATH ${PACKAGE_CONFIG_TEXT})
endif()
endif()

if(AVS_INCLUDE_DIR)
set(Avisynth_FOUND True)
message(STATUS "${PACKAGE_CONFIG_TEXT}: ${AVS_INCLUDE_DIR}")
set(ENABLE_AVISYNTH ON)
else()
set(Avisynth_FOUND False)
set(AVS_INCLUDE_DIR "AVS_INCLUDE_DIR-NOTFOUND" CACHE PATH "${PACKAGE_CONFIG_TEXT}")
message(STATUS "${PACKAGE_CONFIG_TEXT} NOT found")
set(ENABLE_AVISYNTH OFF)
endif()
221 changes: 221 additions & 0 deletions source/input/avs.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,221 @@
/*****************************************************************************
* avs.c: avisynth input
*****************************************************************************
* Copyright (C) 2020 Xinyue Lu
*
* Authors: Xinyue Lu <i@7086.in>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111, USA.
*****************************************************************************/

#include "avs.h"

#define FAIL_IF_ERROR( cond, ... )\
if( cond )\
{\
general_log( NULL, "avs+", X265_LOG_ERROR, __VA_ARGS__ );\
b_fail = true;\
return;\
}

using namespace X265_NS;


lib_path_t AVSInput::convertLibraryPath(std::string path)
{
#if defined(_WIN32_WINNT)
int size_needed = MultiByteToWideChar(CP_UTF8, 0, &path[0], (int)path.size(), NULL, 0);
std::wstring wstrTo( size_needed, 0 );
MultiByteToWideChar(CP_UTF8, 0, &path[0], (int)path.size(), &wstrTo[0], size_needed);
return wstrTo;
#else
return path;
#endif
}

void AVSInput::parseAvsOptions(const char* _options)
{
std::string options {_options}; options += ";";
std::string optSeparator {";"};
std::string valSeparator {"="};
std::map<std::string, int> knownOptions {
{std::string {"library"}, 1}
};

auto start = 0U;
auto end = options.find(optSeparator);

while ((end = options.find(optSeparator, start)) != std::string::npos)
{
auto option = options.substr(start, end - start);
auto valuePos = option.find(valSeparator);
if (valuePos != std::string::npos)
{
auto key = option.substr(0U, valuePos);
auto value = option.substr(valuePos + 1, option.length());
switch (knownOptions[key])
{
case 1:
avs_library_path = convertLibraryPath(value);
general_log(nullptr, "avs+", X265_LOG_INFO, "using external Avisynth library from %s\n", value.c_str());
break;
}
}
else if (option.length() > 0)
{
general_log(nullptr, "avs+", X265_LOG_ERROR, "invalid option \"%s\" ignored\n", option.c_str());
}
start = end + optSeparator.length();
end = options.find(optSeparator, start);
}
}

void AVSInput::load_avs()
{
avs_open();
if (!h->library)
return;
LOAD_AVS_FUNC(avs_clip_get_error);
LOAD_AVS_FUNC(avs_create_script_environment);
LOAD_AVS_FUNC(avs_delete_script_environment);
LOAD_AVS_FUNC(avs_get_frame);
LOAD_AVS_FUNC(avs_get_version);
LOAD_AVS_FUNC(avs_get_video_info);
LOAD_AVS_FUNC(avs_function_exists);
LOAD_AVS_FUNC(avs_invoke);
LOAD_AVS_FUNC(avs_release_clip);
LOAD_AVS_FUNC(avs_release_value);
LOAD_AVS_FUNC(avs_release_video_frame);
LOAD_AVS_FUNC(avs_take_clip);

LOAD_AVS_FUNC(avs_is_y8);
LOAD_AVS_FUNC(avs_is_420);
LOAD_AVS_FUNC(avs_is_422);
LOAD_AVS_FUNC(avs_is_444);
LOAD_AVS_FUNC(avs_bits_per_component);
h->env = h->func.avs_create_script_environment(AVS_INTERFACE_26);
return;
fail:
avs_close();
}

void AVSInput::info_avs()
{
if (!h->func.avs_function_exists(h->env, "VersionString"))
return;
AVS_Value ver = h->func.avs_invoke(h->env, "VersionString", avs_new_value_array(NULL, 0), NULL);
if(avs_is_error(ver))
return;
if(!avs_is_string(ver))
return;
const char *version = avs_as_string(ver);
h->func.avs_release_value(ver);
general_log(NULL, "avs+", X265_LOG_INFO, "%s\n", version);
}

void AVSInput::openfile(InputFileInfo& info)
{
AVS_Value res = h->func.avs_invoke(h->env, "Import", avs_new_value_string(info.filename), NULL);
FAIL_IF_ERROR(avs_is_error(res), "Error loading file: %s\n", avs_as_string(res));
FAIL_IF_ERROR(!avs_is_clip(res), "File didn't return a video clip\n");
h->clip = h->func.avs_take_clip(res, h->env);
const AVS_VideoInfo* vi = h->func.avs_get_video_info(h->clip);
info.width = vi->width;
info.height = vi->height;
info.fpsNum = vi->fps_numerator;
info.fpsDenom = vi->fps_denominator;
info.frameCount = vi->num_frames;
info.depth = h->func.avs_bits_per_component(vi);
h->plane_count = 3;
if(h->func.avs_is_y8(vi))
{
h->plane_count = 1;
info.csp = X265_CSP_I400;
}
else if(h->func.avs_is_420(vi))
{
info.csp = X265_CSP_I420;
}
else if(h->func.avs_is_422(vi))
{
info.csp = X265_CSP_I422;
}
else if(h->func.avs_is_444(vi))
{
info.csp = X265_CSP_I444;
}
else
{
FAIL_IF_ERROR(1, "Video colorspace is not supported\n");
}
if (info.skipFrames)
h->next_frame = info.skipFrames;
}

bool AVSInput::readPicture(x265_picture& pic)
{
AVS_VideoFrame *frm = h->func.avs_get_frame(h->clip, h->next_frame);
const char *err = h->func.avs_clip_get_error(h->clip);
if (err)
{
general_log(NULL, "avs+", X265_LOG_ERROR, "%s occurred while reading frame %d\n", err, h->next_frame);
b_fail = true;
return false;
}
pic.width = _info.width;
pic.height = _info.height;

if (frame_size == 0 || frame_buffer == nullptr) {
frame_size = frm->height * frm->pitch;
if (h->plane_count > 1)
frame_size += frm->heightUV * frm->pitchUV * 2;
frame_buffer = reinterpret_cast<uint8_t*>(x265_malloc(frame_size));
}
pic.framesize = frame_size;

uint8_t* ptr = frame_buffer;
pic.planes[0] = ptr;
pic.stride[0] = frm->pitch;
memcpy(pic.planes[0], frm->vfb->data + frm->offset, frm->pitch * frm->height);
if (h->plane_count > 1)
{
ptr += frm->pitch * frm->height;
pic.planes[1] = ptr;
pic.stride[1] = frm->pitchUV;
memcpy(pic.planes[1], frm->vfb->data + frm->offsetU, frm->pitchUV * frm->heightUV);

ptr += frm->pitchUV * frm->heightUV;
pic.planes[2] = ptr;
pic.stride[2] = frm->pitchUV;
memcpy(pic.planes[2], frm->vfb->data + frm->offsetV, frm->pitchUV * frm->heightUV);
}
pic.colorSpace = _info.csp;
pic.bitDepth = _info.depth;

h->func.avs_release_video_frame(frm);

h->next_frame++;
return true;
}

void AVSInput::release()
{
if (h->clip)
h->func.avs_release_clip(h->clip);
if (h->env)
h->func.avs_delete_script_environment(h->env);
if (h->library)
avs_close();
}
Loading

0 comments on commit 3dbf9c1

Please sign in to comment.