Skip to content

Commit

Permalink
Input: add native AviSynth+ support
Browse files Browse the repository at this point in the history
  • Loading branch information
msg7086 committed Jul 13, 2020
1 parent a1b3bce commit 0bbb886
Show file tree
Hide file tree
Showing 4 changed files with 302 additions and 0 deletions.
13 changes: 13 additions & 0 deletions source/CMakeLists.txt
Expand Up @@ -204,6 +204,7 @@ if(CC STREQUAL "xlc")
endif()
# this option is to enable the inclusion of dynamic HDR10 library to the libx265 compilation
option(ENABLE_HDR10_PLUS "Enable dynamic HDR10 compilation" OFF)
option(ENABLE_AVISYNTH "Enable AviSynth input" ON)
if(MSVC AND (MSVC_VERSION LESS 1800) AND ENABLE_HDR10_PLUS)
message(FATAL_ERROR "MSVC version 12.0 or above required to support hdr10plus")
endif()
Expand All @@ -223,6 +224,11 @@ if(GCC)
message(FATAL_ERROR "gcc version above 4.8 required to support hdr10plus")
endif()
add_definitions(-std=gnu++11)
elseif(ENABLE_AVISYNTH)
if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS "4.8")
message(FATAL_ERROR "gcc version above 4.8 required to support AviSynth")
endif()
add_definitions(-std=gnu++11)
else()
add_definitions(-std=gnu++98)
endif()
Expand Down Expand Up @@ -744,6 +750,10 @@ if(ENABLE_MKV)
add_definitions(-DENABLE_MKV)
endif(ENABLE_MKV)

if(ENABLE_AVISYNTH)
add_definitions(-DENABLE_AVISYNTH)
endif(ENABLE_AVISYNTH)

option(ENABLE_LAVF "Enable LAVF decoder" OFF)
if(ENABLE_LAVF AND ENABLE_CLI)
find_package(FF)
Expand Down Expand Up @@ -784,6 +794,9 @@ if(ENABLE_CLI)
endif()
list(APPEND InputFiles input/lavf.cpp)
endif()
if(ENABLE_AVISYNTH)
list(APPEND InputFiles input/avs.cpp)
endif()
if(ENABLE_LSMASH)
list(APPEND OutputFiles output/mp4.cpp)
endif()
Expand Down
157 changes: 157 additions & 0 deletions source/input/avs.cpp
@@ -0,0 +1,157 @@
/*****************************************************************************
* 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;

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;
general_log(NULL, "avs+", X265_LOG_INFO, "Video colorspace: YUV400 (Y8)\n");
}
else if(h->func.avs_is_420(vi))
{
info.csp = X265_CSP_I420;
general_log(NULL, "avs+", X265_LOG_INFO, "Video colorspace: YUV420 (YV12)\n");
}
else if(h->func.avs_is_422(vi))
{
info.csp = X265_CSP_I422;
general_log(NULL, "avs+", X265_LOG_INFO, "Video colorspace: YUV422 (YV16)\n");
}
else if(h->func.avs_is_444(vi))
{
info.csp = X265_CSP_I444;
general_log(NULL, "avs+", X265_LOG_INFO, "Video colorspace: YUV444 (YV24)\n");
}
else
{
FAIL_IF_ERROR(1, "Video colorspace is not supported\n");
}
general_log(NULL, "avs+", X265_LOG_INFO, "Video depth: %d\n", info.depth);
general_log(NULL, "avs+", X265_LOG_INFO, "Video resolution: %dx%d\n", info.width, info.height);
general_log(NULL, "avs+", X265_LOG_INFO, "Video framerate: %d/%d\n", info.fpsNum, info.fpsDenom);
general_log(NULL, "avs+", X265_LOG_INFO, "Video framecount: %d\n", info.frameCount);
}

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.planes[0] = frm->vfb->data + frm->offset;
pic.stride[0] = frm->pitch;
if (h->plane_count > 1)
{
pic.planes[1] = frm->vfb->data + frm->offsetU;
pic.stride[1] = frm->pitchUV;
pic.planes[2] = frm->vfb->data + frm->offsetV;
pic.stride[2] = frm->pitchUV;
}
pic.colorSpace = _info.csp;
pic.bitDepth = _info.depth;

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();
}
123 changes: 123 additions & 0 deletions source/input/avs.h
@@ -0,0 +1,123 @@
#ifndef X265_AVS_H
#define X265_AVS_H

#include "input.h"
#include <avisynth/avisynth_c.h>

#if _WIN32
#include <windows.h>
typedef HMODULE lib_t;
typedef FARPROC func_t;
#else
#include <dlfcn.h>
typedef void* lib_t;
typedef void* func_t;
#define __stdcall
#endif

#define QUEUE_SIZE 5
#define AVS_INTERFACE_26 6

#define LOAD_AVS_FUNC(name) \
{\
h->func.name = reinterpret_cast<decltype(h->func.name)>((void*)avs_address(#name));\
if (!h->func.name) goto fail;\
}

namespace X265_NS {
// x265 private namespace

typedef struct
{
AVS_Clip *clip;
AVS_ScriptEnvironment *env;
lib_t library;
/* declare function pointers for the utilized functions to be loaded without __declspec,
as the avisynth header does not compensate for this type of usage */
struct
{
const char *(__stdcall *avs_clip_get_error)( AVS_Clip *clip );
AVS_ScriptEnvironment *(__stdcall *avs_create_script_environment)( int version );
void (__stdcall *avs_delete_script_environment)( AVS_ScriptEnvironment *env );
AVS_VideoFrame *(__stdcall *avs_get_frame)( AVS_Clip *clip, int n );
int (__stdcall *avs_get_version)( AVS_Clip *clip );
const AVS_VideoInfo *(__stdcall *avs_get_video_info)( AVS_Clip *clip );
int (__stdcall *avs_function_exists)( AVS_ScriptEnvironment *env, const char *name );
AVS_Value (__stdcall *avs_invoke)( AVS_ScriptEnvironment *env, const char *name,
AVS_Value args, const char **arg_names );
void (__stdcall *avs_release_clip)( AVS_Clip *clip );
void (__stdcall *avs_release_value)( AVS_Value value );
void (__stdcall *avs_release_video_frame)( AVS_VideoFrame *frame );
AVS_Clip *(__stdcall *avs_take_clip)( AVS_Value, AVS_ScriptEnvironment *env );
int (__stdcall *avs_is_y8)(const AVS_VideoInfo * p);
int (__stdcall *avs_is_420)(const AVS_VideoInfo * p);
int (__stdcall *avs_is_422)(const AVS_VideoInfo * p);
int (__stdcall *avs_is_444)(const AVS_VideoInfo * p);
int (__stdcall *avs_bits_per_component)(const AVS_VideoInfo * p);
} func;
int next_frame;
int plane_count;
} avs_hnd_t;

class AVSInput : public InputFile
{
protected:
bool b_fail;
bool b_eof;
avs_hnd_t handle;
avs_hnd_t* h;
InputFileInfo _info;
void load_avs();
void info_avs();
void openfile(InputFileInfo& info);
#if _WIN32
void avs_open() { h->library = LoadLibraryW(L"avisynth"); }
void avs_close() { FreeLibrary(h->library); }
func_t avs_address(LPCSTR func) { return GetProcAddress(h->library, func); }
#else
void avs_open() { h->library = dlopen("libavisynth.so", RTLD_NOW); }
void avs_close() { dlclose(h->library); }
func_t avs_address(const char * func) { return dlsym(h->library, func); }
#endif

public:
AVSInput(InputFileInfo& info)
{
b_fail = false;
b_eof = false;
h = &handle;
memset(h, 0, sizeof(handle));
load_avs();
info_avs();
if (!h->library)
{
b_fail = true;
return;
}
openfile(info);
_info = info;
}
~AVSInput() {}
void release();
bool isEof() const
{
return h->next_frame >= _info.frameCount;
}
bool isFail()
{
return b_fail;
}
void startReader() {}
bool readPicture(x265_picture&);
const char *getName() const
{
return "avs+";
}

int getWidth() const { return _info.width; }

int getHeight() const { return _info.height; }
};
}

#endif // ifndef X265_AVS_H
9 changes: 9 additions & 0 deletions source/input/input.cpp
Expand Up @@ -24,6 +24,9 @@
#include "input.h"
#include "yuv.h"
#include "y4m.h"
#ifdef ENABLE_AVISYNTH
#include "avs.h"
#endif
#ifdef ENABLE_LAVF
#include "lavf.h"
#endif
Expand All @@ -36,6 +39,12 @@ InputFile* InputFile::open(InputFileInfo& info, bool bForceY4m)

if (bForceY4m || (s && !strcmp(s, ".y4m")))
return new Y4MInput(info);

#ifdef ENABLE_AVISYNTH
if (s && !strcmp(s, ".avs"))
return new AVSInput(info);
#endif

#ifdef ENABLE_LAVF
if (s &&
( !strcmp(s, ".mp4")
Expand Down

0 comments on commit 0bbb886

Please sign in to comment.