static int drv_vss_load_core(struct vf_instance *vf) { struct vf_priv_s *p = vf->priv; // First load an empty script to get a VSScript, so that we get the vsapi // and vscore. if (vsscript_createScript(&p->se)) return -1; p->vsapi = vsscript_getVSApi(); p->vscore = vsscript_getCore(p->se); return 0; }
bool outputNode() { if (requests < 1) { const VSCoreInfo *info = vsapi->getCoreInfo(vsscript_getCore(se)); requests = info->numThreads; } const VSVideoInfo *vi = vsapi->getVideoInfo(node); totalFrames = vi->numFrames; if (y4m && (vi->format->colorFamily != cmGray && vi->format->colorFamily != cmYUV)) { errorMessage = "Error: Can only apply y4m headers to YUV and Gray format clips"; fprintf(stderr, "%s\n", errorMessage.c_str()); return true; } std::string y4mFormat; std::string numBits; if (y4m) { if (vi->format->colorFamily == cmGray) { y4mFormat = "mono"; if (vi->format->bitsPerSample > 8) y4mFormat = y4mFormat + std::to_string(vi->format->bitsPerSample); } else if (vi->format->colorFamily == cmYUV) { if (vi->format->subSamplingW == 1 && vi->format->subSamplingH == 1) y4mFormat = "420"; else if (vi->format->subSamplingW == 1 && vi->format->subSamplingH == 0) y4mFormat = "422"; else if (vi->format->subSamplingW == 0 && vi->format->subSamplingH == 0) y4mFormat = "444"; else if (vi->format->subSamplingW == 2 && vi->format->subSamplingH == 2) y4mFormat = "410"; else if (vi->format->subSamplingW == 2 && vi->format->subSamplingH == 0) y4mFormat = "411"; else if (vi->format->subSamplingW == 0 && vi->format->subSamplingH == 1) y4mFormat = "440"; else { fprintf(stderr, "No y4m identifier exists for current format\n"); return true; } if (vi->format->bitsPerSample > 8) y4mFormat = y4mFormat + "p" + std::to_string(vi->format->bitsPerSample); } else { fprintf(stderr, "No y4m identifier exists for current format\n"); return true; } } if (!y4mFormat.empty()) y4mFormat = "C" + y4mFormat + " "; std::string header = "YUV4MPEG2 " + y4mFormat + "W" + std::to_string(vi->width) + " H" + std::to_string(vi->height) + " F" + std::to_string(vi->fpsNum) + ":" + std::to_string(vi->fpsDen) + " Ip A0:0\n"; if (y4m) { if (fwrite(header.c_str(), 1, header.size(), outFile) != header.size()) { errorMessage = "Error: fwrite() call failed when writing initial header, errno: " + std::to_string(errno); outputError = true; return outputError; } } std::unique_lock<std::mutex> lock(mutex); int intitalRequestSize = std::min(requests, totalFrames); requestedFrames = intitalRequestSize; for (int n = 0; n < intitalRequestSize; n++) vsapi->getFrameAsync(n, node, frameDoneCallback, NULL); condition.wait(lock); if (outputError) { fprintf(stderr, "%s\n", errorMessage.c_str()); } return outputError; }
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 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 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; }