static int reconfig(struct vf_instance *vf, struct mp_image_params *in, struct mp_image_params *out) { struct vf_priv_s *p = vf->priv; *out = *in; p->fmt_in = *in; if (reinit_vs(vf) < 0) return -1; const VSVideoInfo *vi = p->vsapi->getVideoInfo(p->out_node); out->w = vi->width; out->h = vi->height; out->imgfmt = mp_from_vs(vi->format->id); if (!out->imgfmt) { MP_FATAL(vf, "Unsupported output format.\n"); destroy_vs(vf); return -1; } struct mp_imgfmt_desc desc = mp_imgfmt_get_desc(in->imgfmt); if (in->w % desc.align_x || in->h % desc.align_y) { MP_FATAL(vf, "VapourSynth does not allow unaligned/cropped video sizes.\n"); destroy_vs(vf); return -1; } return 0; }
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, .d_w = d_width, .d_h = d_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); }
/** If @a loaded_in_frame is set, this was called just to indicate a move inside * a frameset, and we basically just reset the appropriate frame's view_state in * that case. When clicking on a link inside a frame, the frame URI is somehow * updated and added to the files-to-load queue, then ses_forward() is called * with @a loaded_in_frame unset, duplicating the whole frameset's location, * then later the file-to-load callback calls it for the particular frame with * @a loaded_in_frame set. */ struct view_state * ses_forward(struct session *ses, int loaded_in_frame) { struct location *loc = NULL; struct view_state *vs; if (!loaded_in_frame) { free_files(ses); mem_free_set(&ses->search_word, NULL); } x: if (!loaded_in_frame) { loc = mem_calloc(1, sizeof(*loc)); if (!loc) return NULL; copy_struct(&loc->download, &ses->loading); } if (ses->task.target.frame && *ses->task.target.frame) { struct frame *frame; assertm(have_location(ses), "no location yet"); if_assert_failed return NULL; if (!loaded_in_frame) { copy_location(loc, cur_loc(ses)); add_to_history(&ses->history, loc); } frame = ses_find_frame(ses, ses->task.target.frame); if (!frame) { if (!loaded_in_frame) { del_from_history(&ses->history, loc); destroy_location(loc); } mem_free_set(&ses->task.target.frame, NULL); goto x; } vs = &frame->vs; if (!loaded_in_frame) { destroy_vs(vs, 1); init_vs(vs, ses->loading_uri, vs->plain); } else { done_uri(vs->uri); vs->uri = get_uri_reference(ses->loading_uri); if (vs->doc_view) { /* vs->doc_view itself will get detached in * render_document_frames(), but that's too * late for us. */ vs->doc_view->vs = NULL; vs->doc_view = NULL; } #ifdef CONFIG_ECMASCRIPT vs->ecmascript_fragile = 1; #endif } } else {
static void uninit(struct vf_instance *vf) { struct vf_priv_s *p = vf->priv; destroy_vs(vf); p->drv->uninit(vf); pthread_cond_destroy(&p->wakeup); pthread_mutex_destroy(&p->lock); }
static void free_mark_by_index(int i) { assert(is_valid_mark_index(i)); if (!marks[i]) return; destroy_vs(marks[i], 1); mem_free_set(&marks[i], NULL); }
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); }
void goto_mark(unsigned char mark, struct view_state *vs) { int old_current_link; #ifdef CONFIG_ECMASCRIPT struct ecmascript_interpreter *ecmascript; int ecmascript_fragile; #endif struct document_view *doc_view; int i; if (!is_valid_mark_char(mark)) return; i = index_from_char(mark); assert(is_valid_mark_index(i)); /* TODO: Support for cross-document marks. --pasky */ if (!marks[i] || !compare_uri(marks[i]->uri, vs->uri, 0)) return; old_current_link = vs->current_link; #ifdef CONFIG_ECMASCRIPT ecmascript = vs->ecmascript; ecmascript_fragile = vs->ecmascript_fragile; #endif doc_view = vs->doc_view; destroy_vs(vs, 0); copy_vs(vs, marks[i]); vs->doc_view = doc_view; vs->doc_view->vs = vs; #ifdef CONFIG_ECMASCRIPT vs->ecmascript = ecmascript; vs->ecmascript_fragile = ecmascript_fragile; #endif vs->old_current_link = old_current_link; }
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"); p->initializing = true; if (p->drv->load_core(vf) < 0 || !p->vsapi || !p->vscore) { MP_FATAL(vf, "Could not get vapoursynth API handle.\n"); 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) { MP_FATAL(vf, "Could not get our own input node.\n"); goto error; } if (p->vsapi->propSetNode(vars, "video_in", p->in_node, 0)) goto error; int d_w, d_h; mp_image_params_get_dsize(&p->fmt_in, &d_w, &d_h); p->vsapi->propSetInt(vars, "video_in_dw", d_w, 0); p->vsapi->propSetInt(vars, "video_in_dh", d_h, 0); p->vsapi->propSetFloat(vars, "container_fps", vf->chain->container_fps, 0); p->vsapi->propSetFloat(vars, "display_fps", vf->chain->display_fps, 0); if (p->drv->load(vf, vars) < 0) goto error; if (!p->out_node) { MP_FATAL(vf, "Could not get script output node.\n"); 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; } pthread_mutex_lock(&p->lock); p->initializing = false; pthread_mutex_unlock(&p->lock); 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 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; }