static struct mp_image *filter(struct vf_instance *vf, struct mp_image *mpi) { int e_x = vf->priv->exp_x, e_y = vf->priv->exp_y; int e_w = vf->priv->exp_w, e_h = vf->priv->exp_h; if (e_x == 0 && e_y == 0 && e_w == mpi->w && e_h == mpi->h) return mpi; struct mp_image *dmpi = vf_alloc_out_image(vf); mp_image_copy_attributes(dmpi, mpi); struct mp_image cropped = *dmpi; mp_image_crop(&cropped, e_x, e_y, e_x + mpi->w, e_y + mpi->h); mp_image_copy(&cropped, mpi); int e_x2 = e_x + MP_ALIGN_DOWN(mpi->w, mpi->fmt.align_x); int e_y2 = e_y + MP_ALIGN_DOWN(mpi->h, mpi->fmt.align_y); // top border (over the full width) mp_image_clear(dmpi, 0, 0, e_w, e_y); // bottom border (over the full width) mp_image_clear(dmpi, 0, e_y2, e_w, e_h); // left mp_image_clear(dmpi, 0, e_y, e_x, e_y2); // right mp_image_clear(dmpi, e_x2, e_y, e_w, e_y2); talloc_free(mpi); return dmpi; }
// Note: redraw_frame() can call this with NULL. static void draw_image(struct vo *vo, mp_image_t *mpi) { struct xvctx *ctx = vo->priv; wait_for_completion(vo, ctx->num_buffers - 1); struct mp_image xv_buffer = get_xv_buffer(vo, ctx->current_buf); if (mpi) { mp_image_copy(&xv_buffer, mpi); } else { mp_image_clear(&xv_buffer, 0, 0, xv_buffer.w, xv_buffer.h); } mp_image_setrefp(&ctx->original_image, mpi); }
void mp_blur_rgba_sub_bitmap(struct sub_bitmap *d, double gblur) { struct mp_image *tmp1 = mp_image_alloc(IMGFMT_BGRA, d->w, d->h); if (tmp1) { // on OOM, skip region struct mp_image s = {0}; mp_image_setfmt(&s, IMGFMT_BGRA); mp_image_set_size(&s, d->w, d->h); s.stride[0] = d->stride; s.planes[0] = d->bitmap; mp_image_copy(tmp1, &s); mp_image_sw_blur_scale(&s, tmp1, gblur); } talloc_free(tmp1); }
// Note: REDRAW_FRAME can call this with NULL. static void draw_image(struct vo *vo, mp_image_t *mpi) { struct xvctx *ctx = vo->priv; wait_for_completion(vo, ctx->num_buffers - 1); struct mp_image xv_buffer = get_xv_buffer(vo, ctx->current_buf); if (mpi) { mp_image_copy(&xv_buffer, mpi); } else { mp_image_clear(&xv_buffer, 0, 0, xv_buffer.w, xv_buffer.h); } struct mp_osd_res res = osd_res_from_image_params(vo->params); osd_draw_on_image(vo->osd, res, mpi ? mpi->pts : 0, 0, &xv_buffer); if (mpi != ctx->original_image) { talloc_free(ctx->original_image); ctx->original_image = mpi; } }
static void draw_osd(struct vo *vo, struct osd_state *osd) { struct xvctx *ctx = vo->priv; struct mp_image img = get_xv_buffer(vo, ctx->current_buf); struct mp_osd_res res = { .w = ctx->image_width, .h = ctx->image_height, .display_par = 1.0 / vo->aspdat.par, }; osd_draw_on_image(osd, res, osd->vo_pts, 0, &img); } static void wait_for_completion(struct vo *vo, int max_outstanding) { #if HAVE_SHM struct xvctx *ctx = vo->priv; struct vo_x11_state *x11 = vo->x11; if (ctx->Shmem_Flag) { while (x11->ShmCompletionWaitCount > max_outstanding) { if (!ctx->Shm_Warned_Slow) { MP_WARN(vo, "X11 can't keep up! Waiting" " for XShm completion events...\n"); ctx->Shm_Warned_Slow = 1; } mp_sleep_us(1000); vo_x11_check_events(vo); } } #endif } static void flip_page(struct vo *vo) { struct xvctx *ctx = vo->priv; put_xvimage(vo, ctx->xvimage[ctx->current_buf]); /* remember the currently visible buffer */ ctx->current_buf = (ctx->current_buf + 1) % ctx->num_buffers; if (!ctx->Shmem_Flag) XSync(vo->x11->display, False); } static mp_image_t *get_screenshot(struct vo *vo) { struct xvctx *ctx = vo->priv; if (!ctx->original_image) return NULL; return mp_image_new_ref(ctx->original_image); } // Note: redraw_frame() can call this with NULL. static void draw_image(struct vo *vo, mp_image_t *mpi) { struct xvctx *ctx = vo->priv; wait_for_completion(vo, ctx->num_buffers - 1); struct mp_image xv_buffer = get_xv_buffer(vo, ctx->current_buf); if (mpi) { mp_image_copy(&xv_buffer, mpi); } else { mp_image_clear(&xv_buffer, 0, 0, xv_buffer.w, xv_buffer.h); } mp_image_setrefp(&ctx->original_image, mpi); } static int redraw_frame(struct vo *vo) { struct xvctx *ctx = vo->priv; draw_image(vo, ctx->original_image); return true; } static int query_format(struct vo *vo, uint32_t format) { struct xvctx *ctx = vo->priv; uint32_t i; int flag = VFCAP_CSP_SUPPORTED | VFCAP_CSP_SUPPORTED_BY_HW; int fourcc = find_xv_format(format); if (fourcc) { for (i = 0; i < ctx->formats; i++) { if (ctx->fo[i].id == fourcc) return flag; } } return 0; } static void uninit(struct vo *vo) { struct xvctx *ctx = vo->priv; int i; talloc_free(ctx->original_image); if (ctx->ai) XvFreeAdaptorInfo(ctx->ai); ctx->ai = NULL; if (ctx->fo) { XFree(ctx->fo); ctx->fo = NULL; } for (i = 0; i < ctx->num_buffers; i++) deallocate_xvimage(vo, i); // uninit() shouldn't get called unless initialization went past vo_init() vo_x11_uninit(vo); } static int preinit(struct vo *vo) { XvPortID xv_p; int busy_ports = 0; unsigned int i; struct xvctx *ctx = vo->priv; int xv_adaptor = ctx->cfg_xv_adaptor; if (!vo_x11_init(vo)) return -1; struct vo_x11_state *x11 = vo->x11; /* check for Xvideo extension */ unsigned int ver, rel, req, ev, err; if (Success != XvQueryExtension(x11->display, &ver, &rel, &req, &ev, &err)) { MP_ERR(vo, "Xv not supported by this X11 version/driver\n"); goto error; } /* check for Xvideo support */ if (Success != XvQueryAdaptors(x11->display, DefaultRootWindow(x11->display), &ctx->adaptors, &ctx->ai)) { MP_ERR(vo, "XvQueryAdaptors failed.\n"); goto error; } /* check adaptors */ if (ctx->xv_port) { int port_found; for (port_found = 0, i = 0; !port_found && i < ctx->adaptors; i++) { if ((ctx->ai[i].type & XvInputMask) && (ctx->ai[i].type & XvImageMask)) { for (xv_p = ctx->ai[i].base_id; xv_p < ctx->ai[i].base_id + ctx->ai[i].num_ports; ++xv_p) { if (xv_p == ctx->xv_port) { port_found = 1; break; } } } } if (port_found) { if (XvGrabPort(x11->display, ctx->xv_port, CurrentTime)) ctx->xv_port = 0; } else { MP_WARN(vo, "Invalid port parameter, overriding with port 0.\n"); ctx->xv_port = 0; } } for (i = 0; i < ctx->adaptors && ctx->xv_port == 0; i++) { /* check if adaptor number has been specified */ if (xv_adaptor != -1 && xv_adaptor != i) continue; if ((ctx->ai[i].type & XvInputMask) && (ctx->ai[i].type & XvImageMask)) { for (xv_p = ctx->ai[i].base_id; xv_p < ctx->ai[i].base_id + ctx->ai[i].num_ports; ++xv_p) if (!XvGrabPort(x11->display, xv_p, CurrentTime)) { ctx->xv_port = xv_p; MP_VERBOSE(vo, "Using Xv Adapter #%d (%s)\n", i, ctx->ai[i].name); break; } else { MP_WARN(vo, "Could not grab port %i.\n", (int) xv_p); ++busy_ports; } } } if (!ctx->xv_port) { if (busy_ports) MP_ERR(vo, "Could not find free Xvideo port - maybe another process is already\n"\ "using it. Close all video applications, and try again. If that does\n"\ "not help, see 'mpv -vo help' for other (non-xv) video out drivers.\n"); else MP_ERR(vo, "It seems there is no Xvideo support for your video card available.\n"\ "Run 'xvinfo' to verify its Xv support and read\n"\ "DOCS/HTML/en/video.html#xv!\n"\ "See 'mpv -vo help' for other (non-xv) video out drivers.\n"\ "Try -vo x11.\n"); goto error; } if (!xv_init_colorkey(vo)) { goto error; // bail out, colorkey setup failed } xv_enable_vsync(vo); xv_get_max_img_dim(vo, &ctx->max_width, &ctx->max_height); ctx->fo = XvListImageFormats(x11->display, ctx->xv_port, (int *) &ctx->formats); return 0; error: uninit(vo); // free resources return -1; }
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; // The number of frames of our input node is obviously unknown. The user // could for example seek any time, randomly "ending" the clip. // This specific value was suggested by the VapourSynth developer. int enough_for_everyone = INT_MAX / 16; // 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, .numFrames = enough_for_everyone, }; 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); MP_TRACE(vf, "VS asking for frame %d (at %d)\n", frameno, p->in_frameno); while (1) { if (p->shutdown) { p->vsapi->setFilterError("EOF or filter reinit/uninit", frameCtx); MP_DBG(vf, "returning error on EOF/reset\n"); break; } if (p->initializing) { MP_WARN(vf, "Frame requested during init! This is unsupported.\n" "Returning black dummy frame with 0 duration.\n"); ret = alloc_vs_frame(p, &vf->fmt_in); if (!ret) { p->vsapi->setFilterError("Could not allocate VS frame", frameCtx); break; } struct mp_image vsframe = map_vs_frame(p, ret, true); mp_image_clear(&vsframe, 0, 0, vf->fmt_in.w, vf->fmt_in.h); struct mp_image dummy = {0}; mp_image_set_params(&dummy, &vf->fmt_in); set_vs_frame_props(p, ret, &dummy, 0, 1); break; } if (frameno < p->in_frameno) { char msg[180]; snprintf(msg, sizeof(msg), "Frame %d requested, but only have frames starting from %d. " "Try increasing the buffered-frames suboption.", frameno, p->in_frameno); MP_FATAL(vf, "%s\n", msg); p->vsapi->setFilterError(msg, frameCtx); break; } if (frameno >= p->in_frameno + MP_TALLOC_AVAIL(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); if (vf->chain->wakeup_callback) vf->chain->wakeup_callback(vf->chain->wakeup_callback_ctx); continue; } } if (frameno >= p->in_frameno + p->num_buffered) { // If we think EOF was reached, don't wait for new input, and assume // the VS filter has reached EOF. if (p->eof) { p->shutdown = true; continue; } } if (frameno < p->in_frameno + p->num_buffered) { struct mp_image *img = p->buffered[frameno - p->in_frameno]; ret = alloc_vs_frame(p, &img->params); 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); int res = 1e6; int dur = img->pts * res + 0.5; set_vs_frame_props(p, ret, img, dur, res); break; } pthread_cond_wait(&p->wakeup, &p->lock); } pthread_cond_broadcast(&p->wakeup); 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); } // number of getAsyncFrame calls in progress // must be called with p->lock held static int num_requested(struct vf_priv_s *p) { int r = 0; for (int n = 0; n < p->max_requests; n++) r += p->requested[n] == &dummy_img; return r; }
static struct mp_image *filter(struct vf_instance *vf, struct mp_image *mpi) { struct pullup_context *c = vf->priv->ctx; struct pullup_buffer *b; struct pullup_frame *f; int p; int i; double pts = mpi->pts; struct mp_image *dmpi = NULL; if (!vf->priv->init) init_pullup(vf, mpi); if (1) { b = pullup_get_buffer(c, 2); if (!b) { mp_msg(MSGT_VFILTER,MSGL_ERR,"Could not get buffer from pullup!\n"); f = pullup_get_frame(c); pullup_release_frame(f); goto skip; } memcpy_pic(b->planes[0], mpi->planes[0], mpi->w, mpi->h, c->stride[0], mpi->stride[0]); memcpy_pic(b->planes[1], mpi->planes[1], mpi->chroma_width, mpi->chroma_height, c->stride[1], mpi->stride[1]); memcpy_pic(b->planes[2], mpi->planes[2], mpi->chroma_width, mpi->chroma_height, c->stride[2], mpi->stride[2]); } if (mpi->qscale) { memcpy(b->planes[3], mpi->qscale, c->w[3]); memcpy(b->planes[3]+c->w[3], mpi->qscale, c->w[3]); } p = mpi->fields & MP_IMGFIELD_TOP_FIRST ? 0 : (mpi->fields & MP_IMGFIELD_ORDERED ? 1 : 0); if (pts == MP_NOPTS_VALUE) { pullup_submit_field(c, b, p, MP_NOPTS_VALUE); pullup_submit_field(c, b, p^1, MP_NOPTS_VALUE); if (mpi->fields & MP_IMGFIELD_REPEAT_FIRST) pullup_submit_field(c, b, p, MP_NOPTS_VALUE); } else { double delta; if (vf->priv->lastpts == MP_NOPTS_VALUE) delta = 1001.0/60000.0; // delta = field time distance else delta = (pts - vf->priv->lastpts) / 2; if (delta <= 0.0 || delta >= 0.5) delta = 0.0; vf->priv->lastpts = pts; if (mpi->fields & MP_IMGFIELD_REPEAT_FIRST) { pullup_submit_field(c, b, p, pts - delta); pullup_submit_field(c, b, p^1, pts); pullup_submit_field(c, b, p, pts + delta); } else { pullup_submit_field(c, b, p, pts - delta * 0.5); pullup_submit_field(c, b, p^1, pts + delta * 0.5); } } pullup_release_buffer(b, 2); f = pullup_get_frame(c); /* Fake yes for first few frames (buffer depth) to keep from * breaking A/V sync with G1's bad architecture... */ //if (!f) return vf->priv->fakecount ? (--vf->priv->fakecount,1) : 0; if (!f) goto skip; if (f->length < 2) { pullup_release_frame(f); f = pullup_get_frame(c); if (!f) goto skip; if (f->length < 2) { pullup_release_frame(f); if (!(mpi->fields & MP_IMGFIELD_REPEAT_FIRST)) goto skip; f = pullup_get_frame(c); if (!f) goto skip; if (f->length < 2) { pullup_release_frame(f); goto skip; } } } #if 0 /* Average qscale tables from both frames. */ if (mpi->qscale) { for (i=0; i<c->w[3]; i++) { vf->priv->qbuf[i] = (f->ofields[0]->planes[3][i] + f->ofields[1]->planes[3][i+c->w[3]])>>1; } } #else /* Take worst of qscale tables from both frames. */ if (mpi->qscale) { for (i=0; i<c->w[3]; i++) { vf->priv->qbuf[i] = MAX(f->ofields[0]->planes[3][i], f->ofields[1]->planes[3][i+c->w[3]]); } } #endif /* If the frame isn't already exportable... */ if (!f->buffer) pullup_pack_frame(c, f); // NOTE: the copy could probably be avoided by changing or using the // pullup internal buffer management. But right now just do the // safe thing and always copy. Code outside the filter might // hold a buffer reference even if the filter chain is destroyed. dmpi = vf_alloc_out_image(vf); mp_image_copy_attributes(dmpi, mpi); struct mp_image data = *dmpi; data.planes[0] = f->buffer->planes[0]; data.planes[1] = f->buffer->planes[1]; data.planes[2] = f->buffer->planes[2]; data.stride[0] = c->stride[0]; data.stride[1] = c->stride[1]; data.stride[2] = c->stride[2]; mp_image_copy(dmpi, &data); dmpi->pts = f->pts; // Warning: entirely bogus memory management of qscale if (mpi->qscale) { dmpi->qscale = vf->priv->qbuf; dmpi->qstride = mpi->qstride; dmpi->qscale_type = mpi->qscale_type; } pullup_release_frame(f); skip: talloc_free(mpi); return dmpi; }
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 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); MP_DBG(vf, "VS asking for frame %d (at %d)\n", frameno, p->in_frameno); while (1) { if (p->shutdown) { p->vsapi->setFilterError("EOF or filter reinit/uninit", frameCtx); break; } if (frameno < p->in_frameno) { char msg[180]; snprintf(msg, sizeof(msg), "Frame %d requested, but only have frames starting from %d. " "Try increasing the buffered-frames suboption.", frameno, p->in_frameno); MP_FATAL(vf, "%s\n", msg); p->vsapi->setFilterError(msg, 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); } // number of getAsyncFrame calls in progress // must be called with p->lock held static int num_requested(struct vf_priv_s *p) { int r = 0; for (int n = 0; n < p->max_requests; n++) r += p->requested[n] == &dummy_img; return r; }