static int drv_vss_load(struct vf_instance *vf, VSMap *vars) { struct vf_priv_s *p = vf->priv; vsscript_setVariable(p->se, vars); if (vsscript_evaluateFile(&p->se, p->cfg_file, 0)) { MP_FATAL(vf, "Script evaluation failed:\n%s\n", vsscript_getError(p->se)); return -1; } p->out_node = vsscript_getOutput(p->se, 0); return 0; }
int wmain(int argc, wchar_t **argv) { if (_setmode(_fileno(stdout), _O_BINARY) == -1) fprintf(stderr, "Failed to set stdout to binary mode\n"); SetConsoleCtrlHandler(HandlerRoutine, TRUE); #else int main(int argc, char **argv) { #endif if (argc == 2) { if (nstring(argv[1]) == NSTRING("-version")) { if (!vsscript_init()) { fprintf(stderr, "Failed to initialize VapourSynth environment\n"); return 1; } vsapi = vsscript_getVSApi(); if (!vsapi) { fprintf(stderr, "Failed to get VapourSynth API pointer\n"); vsscript_finalize(); return 1; } VSCore *core = vsapi->createCore(0); if (!core) { fprintf(stderr, "Failed to create core\n"); vsscript_finalize(); return 1; } const VSCoreInfo *info = vsapi->getCoreInfo(core); printf("%s", info->versionString); vsapi->freeCore(core); return 0; } } if (argc < 3) { fprintf(stderr, "VSPipe usage:\n"); fprintf(stderr, "Show version info: vspipe -version\n"); fprintf(stderr, "Show script info: vspipe script.vpy - -info\n"); fprintf(stderr, "Write to stdout: vspipe script.vpy - [options]\n"); fprintf(stderr, "Write to file: vspipe script.vpy <outFile> [options]\n"); fprintf(stderr, "Available options:\n"); fprintf(stderr, "Select output index: -index N\n"); fprintf(stderr, "Set number of concurrent frame requests: -requests N\n"); fprintf(stderr, "Add YUV4MPEG headers: -y4m\n"); fprintf(stderr, "Print progress to stderr: -progress\n"); fprintf(stderr, "Show video info: -info (overrides other options)\n"); return 1; } nstring outputFilename = argv[2]; if (outputFilename == NSTRING("-")) { outFile = stdout; } else { #ifdef VS_TARGET_OS_WINDOWS outFile = _wfopen(outputFilename.c_str(), L"wb"); #else outFile = fopen(outputFilename.c_str(), "wb"); #endif if (!outFile) { fprintf(stderr, "Failed to open output for writing\n"); return 1; } } for (int arg = 3; arg < argc; arg++) { nstring argString = argv[arg]; if (argString == NSTRING("-y4m")) { y4m = true; } else if (argString == NSTRING("-info")) { showInfo = true; } else if (argString == NSTRING("-index")) { bool ok = false; if (argc <= arg + 1) { fprintf(stderr, "No index number specified\n"); return 1; } if (!nstringToInt(argv[arg + 1], outputIndex)) { fprintf(stderr, "Couldn't convert %s to an integer\n", nstringToUtf8(argv[arg + 1]).c_str()); return 1; } arg++; } else if (argString == NSTRING("-requests")) { bool ok = false; if (argc <= arg + 1) { fprintf(stderr, "No request number specified\n"); return 1; } if (!nstringToInt(argv[arg + 1], requests)) { fprintf(stderr, "Couldn't convert %s to an integer\n", nstringToUtf8(argv[arg + 1]).c_str()); return 1; } arg++; } else if (argString == NSTRING("-progress")) { printFrameNumber = true; } else { fprintf(stderr, "Unknown argument: %s\n", nstringToUtf8(argString).c_str()); return 1; } } if (!vsscript_init()) { fprintf(stderr, "Failed to initialize VapourSynth environment\n"); return 1; } vsapi = vsscript_getVSApi(); if (!vsapi) { fprintf(stderr, "Failed to get VapourSynth API pointer\n"); vsscript_finalize(); return 1; } std::chrono::time_point<std::chrono::high_resolution_clock> start(std::chrono::high_resolution_clock::now()); if (vsscript_evaluateFile(&se, nstringToUtf8(argv[1]).c_str(), efSetWorkingDir)) { fprintf(stderr, "Script evaluation failed:\n%s\n", vsscript_getError(se)); vsscript_freeScript(se); vsscript_finalize(); return 1; } node = vsscript_getOutput(se, outputIndex); if (!node) { fprintf(stderr, "Failed to retrieve output node. Invalid index specified?\n"); vsscript_freeScript(se); vsscript_finalize(); return 1; } bool error = false; const VSVideoInfo *vi = vsapi->getVideoInfo(node); if (showInfo) { fprintf(outFile, "Width: %d\n", vi->width); fprintf(outFile, "Height: %d\n", vi->height); fprintf(outFile, "Frames: %d\n", vi->numFrames); fprintf(outFile, "FPS: %" PRId64 "/%" PRId64 "\n", vi->fpsNum, vi->fpsDen); if (vi->format) { fprintf(outFile, "Format Name: %s\n", vi->format->name); fprintf(outFile, "Color Family: %s\n", colorFamilyToString(vi->format->colorFamily)); fprintf(outFile, "Bits: %d\n", vi->format->bitsPerSample); fprintf(outFile, "SubSampling W: %d\n", vi->format->subSamplingW); fprintf(outFile, "SubSampling H: %d\n", vi->format->subSamplingH); } else { fprintf(outFile, "Format Name: Variable\n"); } } else { if (!isConstantFormat(vi) || vi->numFrames == 0) { fprintf(stderr, "Cannot output clips with varying dimensions or unknown length\n"); vsapi->freeNode(node); vsscript_freeScript(se); vsscript_finalize(); return 1; } lastFpsReportTime = std::chrono::high_resolution_clock::now(); error = outputNode(); } fflush(outFile); std::chrono::time_point<std::chrono::high_resolution_clock> end(std::chrono::high_resolution_clock::now()); std::chrono::duration<double> elapsedSeconds = end - start; fprintf(stderr, "Output %d frames in %.2f seconds (%.2f fps)\n", outputFrames, elapsedSeconds.count(), outputFrames / elapsedSeconds.count()); vsapi->freeNode(node); vsscript_freeScript(se); vsscript_finalize(); return error; }
static void VS_CC infiltInit(VSMap *in, VSMap *out, void **instanceData, VSNode *node, VSCore *core, const VSAPI *vsapi) { struct vf_instance *vf = *instanceData; struct vf_priv_s *p = vf->priv; // Note: this is called from createFilter, so no need for locking. VSVideoInfo fmt = { .format = p->vsapi->getFormatPreset(mp_to_vs(p->fmt_in.imgfmt), p->vscore), .width = p->fmt_in.w, .height = p->fmt_in.h, }; if (!fmt.format) { p->vsapi->setError(out, "Unsupported input format.\n"); return; } p->vsapi->setVideoInfo(&fmt, 1, node); p->in_node_active = true; } static const VSFrameRef *VS_CC infiltGetFrame(int frameno, int activationReason, void **instanceData, void **frameData, VSFrameContext *frameCtx, VSCore *core, const VSAPI *vsapi) { struct vf_instance *vf = *instanceData; struct vf_priv_s *p = vf->priv; VSFrameRef *ret = NULL; pthread_mutex_lock(&p->lock); while (1) { if (p->shutdown) break; if (frameno < p->in_frameno) { p->vsapi->setFilterError("Requesting a frame too far in the past. " "Try increasing the maxbuffer suboption", frameCtx); break; } if (frameno >= p->in_frameno + MP_TALLOC_ELEMS(p->buffered)) { // Too far in the future. Remove frames, so that the main thread can // queue new frames. if (p->num_buffered) { drain_oldest_buffered_frame(p); pthread_cond_broadcast(&p->wakeup); continue; } } if (frameno < p->in_frameno + p->num_buffered) { struct mp_image *img = p->buffered[frameno - p->in_frameno]; const VSFormat *vsfmt = vsapi->getFormatPreset(mp_to_vs(img->imgfmt), core); ret = vsapi->newVideoFrame(vsfmt, img->w, img->h, NULL, core); if (!ret) { p->vsapi->setFilterError("Could not allocate VS frame", frameCtx); break; } struct mp_image vsframe = map_vs_frame(p, ret, true); mp_image_copy(&vsframe, img); VSMap *map = p->vsapi->getFramePropsRW(ret); if (map) { int res = 1e6; int dur = img->pts * res + 0.5; p->vsapi->propSetInt(map, "_DurationNum", dur, 0); p->vsapi->propSetInt(map, "_DurationDen", res, 0); copy_mp_to_vs_frame_props(p, map, img); } break; } pthread_cond_wait(&p->wakeup, &p->lock); } pthread_mutex_unlock(&p->lock); return ret; } static void VS_CC infiltFree(void *instanceData, VSCore *core, const VSAPI *vsapi) { struct vf_instance *vf = instanceData; struct vf_priv_s *p = vf->priv; pthread_mutex_lock(&p->lock); p->in_node_active = false; pthread_cond_broadcast(&p->wakeup); pthread_mutex_unlock(&p->lock); } static void destroy_vs(struct vf_instance *vf) { struct vf_priv_s *p = vf->priv; // Wait until our frame callback returns. pthread_mutex_lock(&p->lock); p->shutdown = true; pthread_cond_broadcast(&p->wakeup); while (p->getting_frame) pthread_cond_wait(&p->wakeup, &p->lock); pthread_mutex_unlock(&p->lock); if (p->in_node) p->vsapi->freeNode(p->in_node); if (p->out_node) p->vsapi->freeNode(p->out_node); p->in_node = p->out_node = NULL; if (p->se) vsscript_freeScript(p->se); p->se = NULL; p->vsapi = NULL; p->vscore = NULL; assert(!p->in_node_active); p->shutdown = false; talloc_free(p->got_frame); p->got_frame = NULL; // Kill queued frames too for (int n = 0; n < p->num_buffered; n++) talloc_free(p->buffered[n]); p->num_buffered = 0; talloc_free(p->next_image); p->next_image = NULL; p->prev_pts = MP_NOPTS_VALUE; p->out_frameno = p->in_frameno = 0; } static int reinit_vs(struct vf_instance *vf) { struct vf_priv_s *p = vf->priv; VSMap *vars = NULL, *in = NULL, *out = NULL; int res = -1; destroy_vs(vf); // First load an empty script to get a VSScript, so that we get the vsapi // and vscore. if (vsscript_evaluateScript(&p->se, "", NULL, 0)) goto error; p->vsapi = vsscript_getVSApi(); p->vscore = vsscript_getCore(p->se); if (!p->vsapi || !p->vscore) goto error; in = p->vsapi->createMap(); out = p->vsapi->createMap(); vars = p->vsapi->createMap(); if (!in || !out || !vars) goto error; p->vsapi->createFilter(in, out, "Input", infiltInit, infiltGetFrame, infiltFree, fmSerial, 0, vf, p->vscore); int vserr; p->in_node = p->vsapi->propGetNode(out, "clip", 0, &vserr); if (!p->in_node) goto error; if (p->vsapi->propSetNode(vars, "video_in", p->in_node, 0)) goto error; vsscript_setVariable(p->se, vars); if (vsscript_evaluateFile(&p->se, p->cfg_file, 0)) { MP_FATAL(vf, "Script evaluation failed:\n%s\n", vsscript_getError(p->se)); goto error; } p->out_node = vsscript_getOutput(p->se, 0); if (!p->out_node) goto error; const VSVideoInfo *vi = p->vsapi->getVideoInfo(p->out_node); if (!isConstantFormat(vi)) { MP_FATAL(vf, "Video format is required to be constant.\n"); goto error; } res = 0; error: if (p->vsapi) { p->vsapi->freeMap(in); p->vsapi->freeMap(out); p->vsapi->freeMap(vars); } if (res < 0) destroy_vs(vf); return res; } static int config(struct vf_instance *vf, int width, int height, int d_width, int d_height, unsigned int flags, unsigned int fmt) { struct vf_priv_s *p = vf->priv; p->fmt_in = (struct mp_image_params){ .imgfmt = fmt, .w = width, .h = height, }; if (reinit_vs(vf) < 0) return 0; const VSVideoInfo *vi = p->vsapi->getVideoInfo(p->out_node); fmt = mp_from_vs(vi->format->id); if (!fmt) { MP_FATAL(vf, "Unsupported output format.\n"); destroy_vs(vf); return 0; } vf_rescale_dsize(&d_width, &d_height, width, height, vi->width, vi->height); return vf_next_config(vf, vi->width, vi->height, d_width, d_height, flags, fmt); } static int query_format(struct vf_instance *vf, unsigned int fmt) { return mp_to_vs(fmt) != pfNone ? VFCAP_CSP_SUPPORTED : 0; } static int control(vf_instance_t *vf, int request, void *data) { switch (request) { case VFCTRL_SEEK_RESET: if (reinit_vs(vf) < 0) return CONTROL_ERROR; return CONTROL_OK; } return CONTROL_UNKNOWN; } static void uninit(struct vf_instance *vf) { struct vf_priv_s *p = vf->priv; destroy_vs(vf); vsscript_finalize(); pthread_cond_destroy(&p->wakeup); pthread_mutex_destroy(&p->lock); } static int vf_open(vf_instance_t *vf) { struct vf_priv_s *p = vf->priv; if (!vsscript_init()) { MP_FATAL(vf, "Could not initialize VapourSynth scripting.\n"); return 0; } if (!p->cfg_file || !p->cfg_file[0]) { MP_FATAL(vf, "'file' parameter must be set.\n"); return 0; } pthread_mutex_init(&p->lock, NULL); pthread_cond_init(&p->wakeup, NULL); vf->reconfig = NULL; vf->config = config; vf->filter_ext = filter_ext; vf->filter = NULL; vf->query_format = query_format; vf->control = control; vf->uninit = uninit; p->buffered = talloc_array(vf, struct mp_image *, p->cfg_maxbuffer); return 1; } #define OPT_BASE_STRUCT struct vf_priv_s static const m_option_t vf_opts_fields[] = { OPT_STRING("file", cfg_file, 0), OPT_INTRANGE("maxbuffer", cfg_maxbuffer, 0, 1, 9999, OPTDEF_INT(5)), {0} }; const vf_info_t vf_info_vapoursynth = { .description = "vapoursynth bridge", .name = "vapoursynth", .open = vf_open, .priv_size = sizeof(struct vf_priv_s), .options = vf_opts_fields, };
static int reinit_vs(struct vf_instance *vf) { struct vf_priv_s *p = vf->priv; VSMap *vars = NULL, *in = NULL, *out = NULL; int res = -1; destroy_vs(vf); MP_DBG(vf, "initializing...\n"); // First load an empty script to get a VSScript, so that we get the vsapi // and vscore. if (vsscript_evaluateScript(&p->se, "", NULL, 0)) goto error; p->vsapi = vsscript_getVSApi(); p->vscore = vsscript_getCore(p->se); if (!p->vsapi || !p->vscore) goto error; in = p->vsapi->createMap(); out = p->vsapi->createMap(); vars = p->vsapi->createMap(); if (!in || !out || !vars) goto error; p->vsapi->createFilter(in, out, "Input", infiltInit, infiltGetFrame, infiltFree, fmSerial, 0, vf, p->vscore); int vserr; p->in_node = p->vsapi->propGetNode(out, "clip", 0, &vserr); if (!p->in_node) goto error; if (p->vsapi->propSetNode(vars, "video_in", p->in_node, 0)) goto error; vsscript_setVariable(p->se, vars); if (vsscript_evaluateFile(&p->se, p->cfg_file, 0)) { MP_FATAL(vf, "Script evaluation failed:\n%s\n", vsscript_getError(p->se)); goto error; } p->out_node = vsscript_getOutput(p->se, 0); if (!p->out_node) goto error; const VSVideoInfo *vi = p->vsapi->getVideoInfo(p->out_node); if (!isConstantFormat(vi)) { MP_FATAL(vf, "Video format is required to be constant.\n"); goto error; } MP_DBG(vf, "initialized.\n"); res = 0; error: if (p->vsapi) { p->vsapi->freeMap(in); p->vsapi->freeMap(out); p->vsapi->freeMap(vars); } if (res < 0) destroy_vs(vf); return res; }
static int open_file( char *psz_filename, hnd_t *p_handle, video_info_t *info, cli_input_opt_t *opt ) { vs_hnd_t *h = new vs_hnd_t; if( !h ) return -1; FILE *fh = x264_fopen(psz_filename, "rb"); if (!fh) return -1; int b_regular = x264_is_regular_file(fh); fclose(fh); FAIL_IF_ERROR(!b_regular, "VS input is incompatible with non-regular file `%s'\n", psz_filename); FAIL_IF_ERROR(!vsscript_init(), "Failed to initialize VapourSynth environment\n"); h->vsapi = vsscript_getVSApi(); if (!h->vsapi) { fprintf(stderr, "Failed to get VapourSynth API pointer\n"); vsscript_finalize(); return -1; } // Should always succeed if (vsscript_createScript(&h->se)) { fprintf(stderr, "Script environment initialization failed:\n%s\n", vsscript_getError(h->se)); vsscript_freeScript(h->se); vsscript_finalize(); return -1; } std::string strfilename = psz_filename; nstring scriptFilename = s2ws(strfilename); if (vsscript_evaluateFile(&h->se, nstringToUtf8(scriptFilename).c_str(), efSetWorkingDir)) { fprintf(stderr, "Script evaluation failed:\n%s\n", vsscript_getError(h->se)); vsscript_freeScript(h->se); vsscript_finalize(); return -1; } h->node = vsscript_getOutput(h->se, 0);//outputIndex if (!h->node) { fprintf(stderr, "Failed to retrieve output node. Invalid index specified?\n"); vsscript_freeScript(h->se); vsscript_finalize(); return -1; } const VSCoreInfo *vsInfo = h->vsapi->getCoreInfo(vsscript_getCore(h->se)); h->sea = new semaphore(vsInfo->numThreads); const VSVideoInfo *vi = h->vsapi->getVideoInfo(h->node); if (vi->format->colorFamily != cmYUV) { fprintf(stderr, "Can only read YUV format clips"); h->vsapi->freeNode(h->node); vsscript_freeScript(h->se); vsscript_finalize(); return -1; } if (!isConstantFormat(vi)) { fprintf(stderr, "Cannot output clips with varying dimensions\n"); h->vsapi->freeNode(h->node); vsscript_freeScript(h->se); vsscript_finalize(); return -1; } info->width = vi->width; info->height = vi->height; info->fps_num = vi->fpsNum; info->fps_den = vi->fpsDen; info->thread_safe = 1; info->num_frames = vi->numFrames; if (vi->format->subSamplingW == 1 && vi->format->subSamplingH == 1) info->csp = X264_CSP_I420; else if (vi->format->subSamplingW == 1 && vi->format->subSamplingH == 0) info->csp = X264_CSP_I422; else if (vi->format->subSamplingW == 0 && vi->format->subSamplingH == 0) info->csp = X264_CSP_I444; h->bit_depth = vi->format->bitsPerSample; if (h->bit_depth > 8) { info->csp |= X264_CSP_HIGH_DEPTH; } *p_handle = (void*)h; return 0; }
int main(int argc, char **argv) { const VSAPI *vsapi = NULL; VSScript *se = NULL; FILE *outFile = NULL; if (argc != 3) { fprintf(stderr, "Usage: vsscript_example <infile> <outfile>\n"); return 1; } // Open the output file for writing outFile = fopen(argv[2], "wb"); if (!outFile) { fprintf(stderr, "Failed to open output for writing\n"); return 1; } // Initialize VSScript, vsscript_finalize() needs to be called the same number of times as vsscript_init() if (!vsscript_init()) { // VapourSynth probably isn't properly installed at all fprintf(stderr, "Failed to initialize VapourSynth environment\n"); return 1; } // Get a pointer to the normal api struct, exists so you don't have to link with the VapourSynth core library // Failure only happens on very rare API version mismatches vsapi = vsscript_getVSApi(); assert(vsapi); // This line does the actual script evaluation. If se = NULL it will create a new environment if (vsscript_evaluateFile(&se, argv[1], 0)) { fprintf(stderr, "Script evaluation failed:\n%s", vsscript_getError(se)); vsscript_freeScript(se); vsscript_finalize(); return 1; } // Get the clip set as output. It is valid until the out index is re-set/cleared/the script is freed VSNodeRef *node = vsscript_getOutput(se, 0); if (!node) { fprintf(stderr, "Failed to retrieve output node\n"); vsscript_freeScript(se); vsscript_finalize(); return 1; } // Reject hard to handle formats const VSVideoInfo *vi = vsapi->getVideoInfo(node); if (!isConstantFormat(vi) || !vi->numFrames) { fprintf(stderr, "Cannot output clips with varying dimensions or unknown length\n"); vsapi->freeNode(node); vsscript_freeScript(se); vsscript_finalize(); return 1; } // Output all frames char errMsg[1024]; int error = 0; for (int n = 0; n < vi->numFrames; n++) { const VSFrameRef *frame = vsapi->getFrame(n, node, errMsg, sizeof(errMsg)); if (!frame) { // Check if an error happened when getting the frame error = 1; break; } // Loop over every row of every plane write to the file for (int p = 0; p < vi->format->numPlanes; p++) { int stride = vsapi->getStride(frame, p); const uint8_t *readPtr = vsapi->getReadPtr(frame, p); int rowSize = vsapi->getFrameWidth(frame, p) * vi->format->bytesPerSample; int height = vsapi->getFrameHeight(frame, p); for (int y = 0; y < height; y++) { // You should probably handle any fwrite errors here as well fwrite(readPtr, rowSize, 1, outFile); readPtr += stride; } } vsapi->freeFrame(frame); } // Cleanup fclose(outFile); vsapi->freeNode(node); vsscript_freeScript(se); vsscript_finalize(); if (error) { fprintf(stderr, "%s", errMsg); return 1; } return 0; }
int wmain(int argc, wchar_t **argv) { if (_setmode(_fileno(stdout), _O_BINARY) == -1) fprintf(stderr, "Failed to set stdout to binary mode\n"); SetConsoleCtrlHandler(HandlerRoutine, TRUE); #else int main(int argc, char **argv) { #endif nstring outputFilename, scriptFilename; bool showHelp = false; std::map<std::string, std::string> scriptArgs; int startFrame = 0; for (int arg = 1; arg < argc; arg++) { nstring argString = argv[arg]; if (argString == NSTRING("-v") || argString == NSTRING("--version")) { showVersion = true; } else if (argString == NSTRING("-y") || argString == NSTRING("--y4m")) { y4m = true; } else if (argString == NSTRING("-p") || argString == NSTRING("--progress")) { printFrameNumber = true; } else if (argString == NSTRING("-i") || argString == NSTRING("--info")) { showInfo = true; } else if (argString == NSTRING("-h") || argString == NSTRING("--help")) { showHelp = true; } else if (argString == NSTRING("-s") || argString == NSTRING("--start")) { if (argc <= arg + 1) { fprintf(stderr, "No start frame specified\n"); return 1; } if (!nstringToInt(argv[arg + 1], startFrame)) { fprintf(stderr, "Couldn't convert %s to an integer (start)\n", nstringToUtf8(argv[arg + 1]).c_str()); return 1; } if (startFrame < 0) { fprintf(stderr, "Negative start frame specified\n"); return 1; } completedFrames = startFrame; outputFrames = startFrame; requestedFrames = startFrame; lastFpsReportFrame = startFrame; arg++; } else if (argString == NSTRING("-e") || argString == NSTRING("--end")) { if (argc <= arg + 1) { fprintf(stderr, "No end frame specified\n"); return 1; } if (!nstringToInt(argv[arg + 1], totalFrames)) { fprintf(stderr, "Couldn't convert %s to an integer (end)\n", nstringToUtf8(argv[arg + 1]).c_str()); return 1; } if (totalFrames < 0) { fprintf(stderr, "Negative end frame specified\n"); return 1; } totalFrames++; arg++; } else if (argString == NSTRING("-o") || argString == NSTRING("--outputindex")) { if (argc <= arg + 1) { fprintf(stderr, "No output index specified\n"); return 1; } if (!nstringToInt(argv[arg + 1], outputIndex)) { fprintf(stderr, "Couldn't convert %s to an integer (index)\n", nstringToUtf8(argv[arg + 1]).c_str()); return 1; } arg++; } else if (argString == NSTRING("-r") || argString == NSTRING("--requests")) { if (argc <= arg + 1) { fprintf(stderr, "Number of requests not specified\n"); return 1; } if (!nstringToInt(argv[arg + 1], requests)) { fprintf(stderr, "Couldn't convert %s to an integer (requests)\n", nstringToUtf8(argv[arg + 1]).c_str()); return 1; } arg++; } else if (argString == NSTRING("-a") || argString == NSTRING("--arg")) { if (argc <= arg + 1) { fprintf(stderr, "No argument specified\n"); return 1; } std::string aLine = nstringToUtf8(argv[arg + 1]).c_str(); size_t equalsPos = aLine.find("="); if (equalsPos == std::string::npos) { fprintf(stderr, "No value specified for argument: %s\n", aLine.c_str()); return 1; } scriptArgs[aLine.substr(0, equalsPos)] = aLine.substr(equalsPos + 1); arg++; } else if (scriptFilename.empty() && !argString.empty() && argString.substr(0, 1) != NSTRING("-")) { scriptFilename = argString; } else if (outputFilename.empty() && !argString.empty() && (argString == NSTRING("-") || (argString.substr(0, 1) != NSTRING("-")))) { outputFilename = argString; } else { fprintf(stderr, "Unknown argument: %s\n", nstringToUtf8(argString).c_str()); return 1; } } if (showVersion && argc > 2) { fprintf(stderr, "Cannot combine version information with other options\n"); return 1; } else if (showVersion) { return printVersion() ? 0 : 1; } else if (showHelp || argc <= 1) { printHelp(); return 1; } else if (scriptFilename.empty()) { fprintf(stderr, "No script file specified\n"); return 1; } else if (outputFilename.empty()) { fprintf(stderr, "No output file specified\n"); return 1; } if (outputFilename == NSTRING("-")) { outFile = stdout; } else { #ifdef VS_TARGET_OS_WINDOWS outFile = _wfopen(outputFilename.c_str(), L"wb"); #else outFile = fopen(outputFilename.c_str(), "wb"); #endif if (!outFile) { fprintf(stderr, "Failed to open output for writing\n"); return 1; } } if (!vsscript_init()) { fprintf(stderr, "Failed to initialize VapourSynth environment\n"); return 1; } vsapi = vsscript_getVSApi(); if (!vsapi) { fprintf(stderr, "Failed to get VapourSynth API pointer\n"); vsscript_finalize(); return 1; } // Should always succeed if (vsscript_createScript(&se)) { fprintf(stderr, "Script environment initialization failed:\n%s\n", vsscript_getError(se)); vsscript_freeScript(se); vsscript_finalize(); return 1; } { VSMap *foldedArgs = vsapi->createMap(); for (const auto &iter : scriptArgs) vsapi->propSetData(foldedArgs, iter.first.c_str(), iter.second.c_str(), static_cast<int>(iter.second.size()), paAppend); vsscript_setVariable(se, foldedArgs); vsapi->freeMap(foldedArgs); } start = std::chrono::high_resolution_clock::now(); if (vsscript_evaluateFile(&se, nstringToUtf8(scriptFilename).c_str(), efSetWorkingDir)) { fprintf(stderr, "Script evaluation failed:\n%s\n", vsscript_getError(se)); vsscript_freeScript(se); vsscript_finalize(); return 1; } node = vsscript_getOutput(se, outputIndex); if (!node) { fprintf(stderr, "Failed to retrieve output node. Invalid index specified?\n"); vsscript_freeScript(se); vsscript_finalize(); return 1; } bool error = false; const VSVideoInfo *vi = vsapi->getVideoInfo(node); if (showInfo) { if (vi->width && vi->height) { fprintf(outFile, "Width: %d\n", vi->width); fprintf(outFile, "Height: %d\n", vi->height); } else { fprintf(outFile, "Width: Variable\n"); fprintf(outFile, "Height: Variable\n"); } if (vi->numFrames) fprintf(outFile, "Frames: %d\n", vi->numFrames); else fprintf(outFile, "Frames: Unknown\n"); if (vi->fpsNum && vi->fpsDen) fprintf(outFile, "FPS: %" PRId64 "/%" PRId64 " (%.3f fps)\n", vi->fpsNum, vi->fpsDen, vi->fpsNum/(double)vi->fpsDen); else fprintf(outFile, "FPS: Variable\n"); if (vi->format) { fprintf(outFile, "Format Name: %s\n", vi->format->name); fprintf(outFile, "Color Family: %s\n", colorFamilyToString(vi->format->colorFamily)); fprintf(outFile, "Bits: %d\n", vi->format->bitsPerSample); fprintf(outFile, "SubSampling W: %d\n", vi->format->subSamplingW); fprintf(outFile, "SubSampling H: %d\n", vi->format->subSamplingH); } else { fprintf(outFile, "Format Name: Variable\n"); } } else { const VSVideoInfo *vi = vsapi->getVideoInfo(node); if (totalFrames == -1) totalFrames = vi->numFrames; if ((vi->numFrames && vi->numFrames < totalFrames) || completedFrames >= totalFrames) { fprintf(stderr, "Invalid range of frames to output specified:\nfirst: %d\nlast: %d\nclip length: %d\nframes to output: %d\n", completedFrames, totalFrames, vi->numFrames, totalFrames - completedFrames); vsapi->freeNode(node); vsscript_freeScript(se); vsscript_finalize(); return 1; } if (!isConstantFormat(vi) || !totalFrames) { fprintf(stderr, "Cannot output clips with varying dimensions or unknown length\n"); vsapi->freeNode(node); vsscript_freeScript(se); vsscript_finalize(); return 1; } lastFpsReportTime = std::chrono::high_resolution_clock::now();; error = outputNode(); } fflush(outFile); if (!showInfo) { int totalFrames = outputFrames - startFrame; std::chrono::duration<double> elapsedSeconds = std::chrono::high_resolution_clock::now() - start; fprintf(stderr, "Output %d frames in %.2f seconds (%.2f fps)\n", totalFrames, elapsedSeconds.count(), totalFrames / elapsedSeconds.count()); } vsapi->freeNode(node); vsscript_freeScript(se); vsscript_finalize(); return error ? 1 : 0; }