Advertisement
DJATOM

Untitled

Nov 20th, 2019
160
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 14.73 KB | None | 0 0
  1. /*****************************************************************************
  2.  * vpy.c: VapourSynth input
  3.  *****************************************************************************
  4.  * Copyright (C) 2009-2018 x264 project
  5.  *
  6.  * Author: Vladimir Kontserenko <djatom@beatrice-raws.org>
  7.  * Some portions of code and ideas taken for avs.c, y4m.c files, "ffmpeg demuxer"
  8.  * proposed at doom9 thread and from rigaya's NVEnc codebase.
  9.  *
  10.  * This program is free software; you can redistribute it and/or modify
  11.  * it under the terms of the GNU General Public License as published by
  12.  * the Free Software Foundation; either version 2 of the License, or
  13.  * (at your option) any later version.
  14.  *
  15.  * This program is distributed in the hope that it will be useful,
  16.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  17.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  18.  * GNU General Public License for more details.
  19.  *
  20.  * You should have received a copy of the GNU General Public License
  21.  * along with this program; if not, write to the Free Software
  22.  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111, USA.
  23.  *
  24.  * This program is also available under a commercial proprietary license.
  25.  * For more information, contact us at licensing@x264.com.
  26.  *****************************************************************************/
  27.  
  28. #include "input.h"
  29.  
  30. #ifdef _MSC_VER
  31. typedef intptr_t atomic_int;
  32. #define atomic_load(object) _InterlockedCompareExchange64(object, 0, 0)
  33. #define atomic_fetch_add(object, operand) _InterlockedExchangeAdd64(object, operand)
  34. #define atomic_fetch_sub(object, operand) _InterlockedExchangeAdd64(object, -(operand))
  35. #else
  36. #include <stdatomic.h>
  37. #endif
  38. #include "extras/VSScript.h"
  39. #include "extras/VSHelper.h"
  40.  
  41. #ifdef _M_IX86
  42. #define VPY_X64 0
  43. #else
  44. #define VPY_X64 1
  45. #endif
  46.  
  47. #ifdef __unix__
  48. #include <dlfcn.h>
  49. #include <unistd.h>
  50. #include <ctype.h>
  51. #define vs_sleep() usleep(500)
  52. #define vs_strtok strtok_r
  53. #define vs_sscanf sscanf
  54. #ifdef __MACH__
  55. #define vs_open() dlopen( "libvapoursynth-script.dylib", RTLD_NOW )
  56. #else
  57. #define vs_open() dlopen( "libvapoursynth-script.so", RTLD_NOW )
  58. #endif
  59. #define vs_close dlclose
  60. #define vs_address dlsym
  61. #else
  62. #define vs_sleep() Sleep(500)
  63. #define vs_strtok strtok_s
  64. #define vs_sscanf sscanf_s
  65. #define vs_open() LoadLibraryW( L"vsscript" )
  66. #define vs_close FreeLibrary
  67. #define vs_address GetProcAddress
  68. #endif
  69.  
  70. #define DECLARE_VS_FUNC(name) func_##name name
  71.  
  72. #define LOAD_VS_FUNC(name, namex86)\
  73. {\
  74.     h->func.name = (void*)vs_address( h->library, (VPY_X64) ? #name : namex86 );\
  75.     if( !h->func.name )\
  76.         goto fail;\
  77. }
  78.  
  79. #define FAIL_IF_ERROR( cond, ... ) FAIL_IF_ERR( cond, "vpy", __VA_ARGS__ )
  80.  
  81. typedef int (VS_CC *func_vsscript_init)(void);
  82. typedef int (VS_CC *func_vsscript_finalize)(void);
  83. typedef int (VS_CC *func_vsscript_evaluateFile)(VSScript **handle, const char *scriptFilename, int flags);
  84. typedef void (VS_CC *func_vsscript_freeScript)(VSScript *handle);
  85. typedef const char * (VS_CC *func_vsscript_getError)(VSScript *handle);
  86. typedef VSNodeRef * (VS_CC *func_vsscript_getOutput)(VSScript *handle, int index);
  87. typedef VSCore * (VS_CC *func_vsscript_getCore)(VSScript *handle);
  88. typedef const VSAPI * (VS_CC *func_vsscript_getVSApi2)(int version);
  89.  
  90. typedef struct VapourSynthContext {
  91.     void *library;
  92.     const VSAPI *vsapi;
  93.     VSScript *script;
  94.     VSNodeRef *node;
  95.     int curr_frame;
  96.     int ncpu;
  97.     atomic_int async_pending;
  98.     int num_frames;
  99.     int bit_depth;
  100.     uint64_t plane_size[3];
  101.     int uc_depth;
  102.     int vfr;
  103.     uint64_t timebase_num;
  104.     uint64_t timebase_den;
  105.     int64_t currentTimecodeNum;
  106.     int64_t currentTimecodeDen;
  107.     struct
  108.     {
  109.         DECLARE_VS_FUNC( vsscript_init );
  110.         DECLARE_VS_FUNC( vsscript_finalize );
  111.         DECLARE_VS_FUNC( vsscript_evaluateFile );
  112.         DECLARE_VS_FUNC( vsscript_freeScript );
  113.         DECLARE_VS_FUNC( vsscript_getError );
  114.         DECLARE_VS_FUNC( vsscript_getOutput );
  115.         DECLARE_VS_FUNC( vsscript_getCore );
  116.         DECLARE_VS_FUNC( vsscript_getVSApi2 );
  117.     } func;
  118. } VapourSynthContext;
  119.  
  120. static int custom_vs_load_library( VapourSynthContext *h )
  121. {
  122.     h->library = vs_open();
  123.     if( !h->library )
  124.         return -1;
  125.     LOAD_VS_FUNC( vsscript_init, "_vsscript_init@0" );
  126.     LOAD_VS_FUNC( vsscript_finalize, "_vsscript_finalize@0" );
  127.     LOAD_VS_FUNC( vsscript_evaluateFile, "_vsscript_evaluateFile@12" );
  128.     LOAD_VS_FUNC( vsscript_freeScript, "_vsscript_freeScript@4" );
  129.     LOAD_VS_FUNC( vsscript_getError, "_vsscript_getError@4" );
  130.     LOAD_VS_FUNC( vsscript_getOutput, "_vsscript_getOutput@8" );
  131.     LOAD_VS_FUNC( vsscript_getCore, "_vsscript_getCore@4" );
  132.     LOAD_VS_FUNC( vsscript_getVSApi2, "_vsscript_getVSApi2@4" );
  133.     return 0;
  134. fail:
  135.     vs_close( h->library );
  136.     h->library = NULL;
  137.     return -1;
  138. }
  139.  
  140. static void VS_CC async_callback( void *user_data, const VSFrameRef *f, int n, VSNodeRef *node, const char *error_msg )
  141. {
  142.     VapourSynthContext *h = user_data;
  143.  
  144.     if (!f) {
  145.         x264_cli_log( "vpy", X264_LOG_WARNING, "async frame request failed: %s\n", error_msg );
  146.     }
  147.  
  148.     h->vsapi->freeFrame( f );
  149.     atomic_fetch_sub( &h->async_pending, 1 );
  150. }
  151.  
  152. /* slightly modified rigaya's VersionString parser */
  153. int get_core_revision(const char *vsVersionString)
  154. {
  155.     char *api_info = NULL;
  156.     char buf[1024];
  157.     strcpy( buf, vsVersionString );
  158.     for ( char *p = buf, *q = NULL, *r = NULL; NULL != ( q = vs_strtok( p, "\n", &r ) ); ) {
  159.         if ( NULL != ( api_info = strstr( q, "Core" ) ) ) {
  160.             strcpy( buf, api_info );
  161.             for ( char *s = buf; *s; s++ )
  162.                 *s = (char)tolower( *s );
  163.             int rev = 0;
  164.             return ( 1 == vs_sscanf( buf, "core r%d", &rev ) ) ? rev : 0;
  165.         }
  166.         p = NULL;
  167.     }
  168.     return 0;
  169. }
  170.  
  171. static int open_file( char *psz_filename, hnd_t *p_handle, video_info_t *info, cli_input_opt_t *opt )
  172. {
  173.     FILE *fh = x264_fopen( psz_filename, "r" );
  174.     if( !fh )
  175.         return -1;
  176.     int b_regular = x264_is_regular_file( fh );
  177.     fclose( fh );
  178.     FAIL_IF_ERROR( !b_regular, "VPY input is incompatible with non-regular file `%s'\n", psz_filename );
  179.  
  180.     VapourSynthContext *h = calloc( 1, sizeof(VapourSynthContext) );
  181.     if( !h )
  182.         return -1;
  183.     FAIL_IF_ERROR( custom_vs_load_library( h ), "failed to load VapourSynth\n" );
  184.     if( !h->func.vsscript_init() )
  185.         FAIL_IF_ERROR( 1, "failed to initialize VapourSynth environment\n" );
  186.     h->vsapi = h->func.vsscript_getVSApi2( VAPOURSYNTH_API_VERSION );
  187.     FAIL_IF_ERROR( !h->vsapi, "failed to get vapoursynth API\n" );
  188.     if( h->func.vsscript_evaluateFile( &h->script, (const char *)psz_filename, efSetWorkingDir ) )
  189.         FAIL_IF_ERROR( 1, "Can't evaluate script: %s\n",  h->func.vsscript_getError( h->script ) );
  190.     h->node = h->func.vsscript_getOutput( h->script, 0 );
  191.     FAIL_IF_ERROR( !h->node, "`%s' has no video data\n", psz_filename );
  192.  
  193.     const VSCoreInfo *core_info = h->vsapi->getCoreInfo( h->func.vsscript_getCore( h->script ) );
  194.     const VSVideoInfo *vi = h->vsapi->getVideoInfo( h->node );
  195.     FAIL_IF_ERROR( !isConstantFormat(vi), "only constant video formats are supported\n" );
  196.     x264_cli_log( "vpy", X264_LOG_INFO, "using VapourSynth Video Processing Library Core R%d\n", get_core_revision( core_info->versionString ) );
  197.     info->width = vi->width;
  198.     info->height = vi->height;
  199.     info->vfr = h->vfr = 0;
  200.  
  201.     /* Not that fancy code, but at least it's working. Any refactoring will be nice. */
  202.     char errbuf[256];
  203.     const VSFrameRef *frame0 = NULL;
  204.     frame0 = h->vsapi->getFrame( 0, h->node, errbuf, sizeof(errbuf) );
  205.     FAIL_IF_ERROR( !frame0, "%s occurred while getting frame 0\n", errbuf );
  206.     const VSMap *props = h->vsapi->getFramePropsRO( frame0 );
  207.     int err_sar_num, err_sar_den;
  208.     int64_t sar_num = h->vsapi->propGetInt( props, "_SARNum", 0, &err_sar_num );
  209.     int64_t sar_den = h->vsapi->propGetInt( props, "_SARDen", 0, &err_sar_den );
  210.     info->sar_height = sar_den;
  211.     info->sar_width  = sar_num;
  212.     if( vi->fpsNum == 0 && vi->fpsDen == 0 ) {
  213.         /* There are no FPS data with native VFR videos, so let's grab it from frame 0. */
  214.         int err_num, err_den;
  215.         int64_t fps_num = h->vsapi->propGetInt( props, "_DurationNum", 0, &err_num );
  216.         int64_t fps_den = h->vsapi->propGetInt( props, "_DurationDen", 0, &err_den );
  217.         FAIL_IF_ERROR( (err_num || err_den), "missing FPS values at frame 0" );
  218.         FAIL_IF_ERROR( !fps_den, "FPS denominator is zero at frame 0" );
  219.         /* Frame props are actually placed opposite, Idk why... */
  220.         info->fps_num = fps_den;
  221.         info->fps_den = fps_num;
  222.         /*
  223.            Idk how to calculate optimal timebase here, as we need extra path to read all frame props...
  224.            I think that's redundant, so just hardcode the most optimal value. Hope that's enough.
  225.         */
  226.         info->timebase_num = h->timebase_num = 1;
  227.         info->timebase_den = h->timebase_den = 10000000;
  228.         h->currentTimecodeNum = 0;
  229.         h->currentTimecodeDen = 1;
  230.         info->vfr = h->vfr = 1;
  231.     } else {
  232.         info->fps_num = vi->fpsNum;
  233.         info->fps_den = vi->fpsDen;
  234.     }
  235.     h->vsapi->freeFrame( frame0 ); // what a waste, but whatever
  236.  
  237.     h->num_frames = info->num_frames = vi->numFrames;
  238.     h->bit_depth = vi->format->bitsPerSample;
  239.     info->thread_safe = 1;
  240.     h->ncpu = core_info->numThreads;
  241.     h->uc_depth = (h->bit_depth & 7) && (vi->format->colorFamily == cmYUV || vi->format->colorFamily == cmYCoCg);
  242.  
  243.     if( vi->format->id == pfRGB48 )
  244.         info->csp = X264_CSP_BGR | X264_CSP_VFLIP | X264_CSP_HIGH_DEPTH;
  245.     else if( vi->format->id == pfRGB24 )
  246.         info->csp = X264_CSP_BGR | X264_CSP_VFLIP;
  247.     else if( vi->format->id == pfYUV444P9 || vi->format->id == pfYUV444P10 || vi->format->id == pfYUV444P12 || vi->format->id == pfYUV444P14 || vi->format->id == pfYUV444P16)
  248.         info->csp = X264_CSP_I444 | X264_CSP_HIGH_DEPTH;
  249.     else if( vi->format->id == pfYUV422P9 || vi->format->id == pfYUV422P10 || vi->format->id == pfYUV422P12 || vi->format->id == pfYUV422P14 || vi->format->id == pfYUV422P16)
  250.         info->csp = X264_CSP_I422 | X264_CSP_HIGH_DEPTH;
  251.     else if( vi->format->id == pfYUV420P9 || vi->format->id == pfYUV420P10 || vi->format->id == pfYUV420P12 || vi->format->id == pfYUV420P14 || vi->format->id == pfYUV420P16)
  252.         info->csp = X264_CSP_I420 | X264_CSP_HIGH_DEPTH;
  253.     else if( vi->format->id == pfYUV444P8 )
  254.         info->csp = X264_CSP_I444;
  255.     else if( vi->format->id == pfYUV422P8 )
  256.         info->csp = X264_CSP_I422;
  257.     else if( vi->format->id == pfYUV420P8 )
  258.         info->csp = X264_CSP_I420;
  259.     else
  260.         FAIL_IF_ERROR( 1, "not supported pixel type: %s\n", vi->format->name );
  261.  
  262.     /* bitdepth upconversion stuff */
  263.     if( h->uc_depth ) {
  264.         const x264_cli_csp_t *cli_csp = x264_cli_get_csp( info->csp );
  265.         for( int i = 0; i < cli_csp->planes; i++ ) {
  266.             h->plane_size[i] = x264_cli_pic_plane_size( info->csp, info->width, info->height, i );
  267.             h->plane_size[i] /= x264_cli_csp_depth_factor( info->csp );
  268.         }
  269.     }
  270.  
  271.     *p_handle = h;
  272.  
  273.     return 0;
  274. }
  275.  
  276. static int picture_alloc( cli_pic_t *pic, hnd_t handle, int csp, int width, int height )
  277. {
  278.     if( x264_cli_pic_alloc( pic, X264_CSP_NONE, width, height ) )
  279.         return -1;
  280.     pic->img.csp = csp;
  281.     const x264_cli_csp_t *cli_csp = x264_cli_get_csp( csp );
  282.     if( cli_csp )
  283.         pic->img.planes = cli_csp->planes;
  284.     return 0;
  285. }
  286.  
  287. static int read_frame( cli_pic_t *pic, hnd_t handle, int i_frame )
  288. {
  289.     static const int planes[3] = { 0, 1, 2 };
  290.     char errbuf[256];
  291.     const VSFrameRef *frm = NULL;
  292.     VapourSynthContext *h = handle;
  293.  
  294.     if( i_frame >= h->num_frames )
  295.         return -1;
  296.  
  297.     /* explicitly cast away the const attribute to avoid a warning */
  298.     frm = pic->opaque = (VSFrameRef*)h->vsapi->getFrame( i_frame, h->node, errbuf, sizeof(errbuf) );
  299.     FAIL_IF_ERROR( !frm, "%s occurred while reading frame %d\n", errbuf, i_frame );
  300.  
  301.     /* Prefetch the subsequent frames. */
  302.     for ( int i = 0; i < h->ncpu + 2; ++i ) {
  303.         if ( i >= h->num_frames - i_frame )
  304.             break;
  305.         h->vsapi->getFrameAsync( i_frame + i, h->node, async_callback, h );
  306.         atomic_fetch_add( &h->async_pending, 1 );
  307.     }
  308.  
  309.     for( int i = 0; i < pic->img.planes; i++ ) {
  310.         /* explicitly cast away the const attribute to avoid a warning */
  311.         pic->img.plane[i] = (uint8_t*)h->vsapi->getReadPtr( frm, planes[i] );
  312.         pic->img.stride[i] = h->vsapi->getStride( frm, planes[i] );
  313.         if( h->uc_depth ) {
  314.             /* upconvert non 16bit high depth planes to 16bit using the same
  315.              * algorithm as used in the depth filter. */
  316.             uint16_t *plane = (uint16_t*)pic->img.plane[i];
  317.             uint64_t pixel_count = h->plane_size[i];
  318.             int lshift = 16 - h->bit_depth;
  319.             for( uint64_t j = 0; j < pixel_count; j++ )
  320.                 plane[j] = plane[j] << lshift;
  321.         }
  322.     }
  323.     if ( h->vfr ) {
  324.         /* Adapted from vspipe timecodes generator and lavf.c vfr part */
  325.         pic->pts = ( h->currentTimecodeNum * h->timebase_den / h->currentTimecodeDen ); // hope it will fit
  326.         pic->duration = 0;
  327.         const VSMap *props = h->vsapi->getFramePropsRO( frm );
  328.         int err_num, err_den;
  329.         int64_t duration_num = h->vsapi->propGetInt( props, "_DurationNum", 0, &err_num );
  330.         int64_t duration_den = h->vsapi->propGetInt( props, "_DurationDen", 0, &err_den );
  331.         FAIL_IF_ERROR( (err_num || err_den), "missing duration at frame %d", i_frame );
  332.         FAIL_IF_ERROR( !duration_den, "duration denominator is zero at frame %d", i_frame );
  333.         vs_addRational( &h->currentTimecodeNum, &h->currentTimecodeDen, duration_num, duration_den );
  334.     }
  335.     return 0;
  336. }
  337.  
  338. static int release_frame( cli_pic_t *pic, hnd_t handle )
  339. {
  340.     VapourSynthContext *h = handle;
  341.     h->vsapi->freeFrame( pic->opaque );
  342.     return 0;
  343. }
  344.  
  345. static void picture_clean( cli_pic_t *pic, hnd_t handle )
  346. {
  347.     memset( pic, 0, sizeof(cli_pic_t) );
  348. }
  349.  
  350. static int close_file( hnd_t handle )
  351. {
  352.     VapourSynthContext *h = handle;
  353.  
  354.     /* Wait for any async requests to complete. */
  355.     atomic_int out;
  356.     while ( out = atomic_load( &h->async_pending ) ) {
  357.         x264_cli_log( "vpy", X264_LOG_INFO, " %d %s pending...\n", out, (out == 1 ? "frame" : "frames") );
  358.         vs_sleep();
  359.     }
  360.  
  361.     h->vsapi->freeNode( h->node );
  362.     h->func.vsscript_freeScript( h->script );
  363.     h->func.vsscript_finalize();
  364.  
  365.     if( h->library )
  366.         vs_close( h->library );
  367.  
  368.     free( h );
  369.     return 0;
  370. }
  371.  
  372. const cli_input_t vpy_input = { open_file, picture_alloc, read_frame, release_frame, picture_clean, close_file };
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement