summaryrefslogtreecommitdiffstats
path: root/video/filter
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2014-05-14 20:41:31 +0200
committerwm4 <wm4@nowhere>2014-05-14 20:42:04 +0200
commitbc6959c35d4a421c920e1d7ce22fb9f339a9a8fb (patch)
tree67d895d0ba16cb450f86842e5c628ea7046161cd /video/filter
parentd6774515a73f06a6c9b9780c3ba911aa5bd3e907 (diff)
downloadmpv-bc6959c35d4a421c920e1d7ce22fb9f339a9a8fb.tar.bz2
mpv-bc6959c35d4a421c920e1d7ce22fb9f339a9a8fb.tar.xz
vf_vapoursynth: allow parallel processing
VapourSynth won't just filter multiple frames at once on its own. You have to request multiple frames at once manually. This is what this commit introduces: a sub-option controls how many frames will be requested at once. This also changes the semantics of the maxbuffer sub- option, now renamed to buffered-frames.
Diffstat (limited to 'video/filter')
-rw-r--r--video/filter/vf_vapoursynth.c118
1 files changed, 81 insertions, 37 deletions
diff --git a/video/filter/vf_vapoursynth.c b/video/filter/vf_vapoursynth.c
index 9ec9ae9e2a..4885ff26bc 100644
--- a/video/filter/vf_vapoursynth.c
+++ b/video/filter/vf_vapoursynth.c
@@ -52,11 +52,12 @@ struct vf_priv_s {
struct mp_image *next_image;// used to compute frame duration of oldest image
struct mp_image **buffered; // oldest image first
int num_buffered;
- double prev_pts; // pts of last frame returned
int in_frameno; // frame number of buffered[0] (the oldest)
- int out_frameno; // frame number of last requested frame
- bool getting_frame; // getAsyncFrame is in progress
- struct mp_image *got_frame; // frame callback result
+ int out_frameno; // frame number of first requested/ready frame
+ double out_pts; // pts corresponding to first requested/ready frame
+ struct mp_image **requested;// frame callback results (can point to dummy_img)
+ // requested[0] is the frame to return first
+ int max_requests; // upper bound for requested[] array
bool failed; // frame callback returned with an error
bool shutdown; // ask node to return
bool in_node_active; // node might still be called
@@ -64,8 +65,12 @@ struct vf_priv_s {
// --- options
char *cfg_file;
int cfg_maxbuffer;
+ int cfg_maxrequests;
};
+// priv->requested[n] points to this if a request for frame n is in-progress
+static const struct mp_image dummy_img;
+
struct mpvs_fmt {
VSPresetFormat vs;
int mp;
@@ -170,6 +175,16 @@ static void drain_oldest_buffered_frame(struct vf_priv_s *p)
p->in_frameno++;
}
+// 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 void VS_CC vs_frame_done(void *userData, const VSFrameRef *f, int n,
VSNodeRef *node, const char *errorMsg)
{
@@ -177,10 +192,14 @@ static void VS_CC vs_frame_done(void *userData, const VSFrameRef *f, int n,
struct vf_priv_s *p = vf->priv;
pthread_mutex_lock(&p->lock);
- assert(p->getting_frame);
- assert(!p->got_frame);
- p->getting_frame = false;
+ // If these assertions fail, n is an unrequested frame.
+ assert(n >= p->out_frameno && n < p->out_frameno + p->max_requests);
+ int index = n - p->out_frameno;
+ assert(p->requested[index] == &dummy_img);
+ MP_DBG(vf, "filtered frame %d (%d)\n", n, index);
+
+ struct mp_image *res = NULL;
if (f) {
struct mp_image img = map_vs_frame(p, f, false);
img.pts = MP_NOPTS_VALUE;
@@ -189,19 +208,18 @@ static void VS_CC vs_frame_done(void *userData, const VSFrameRef *f, int n,
int err1, err2;
int num = p->vsapi->propGetInt(map, "_DurationNum", 0, &err1);
int den = p->vsapi->propGetInt(map, "_DurationDen", 0, &err2);
- if (!err1 && !err2 && p->prev_pts != MP_NOPTS_VALUE) {
- img.pts = p->prev_pts;
- p->prev_pts += num / (double)den;
- }
+ if (!err1 && !err2)
+ img.pts = num / (double)den; // abusing pts for frame length
}
if (img.pts == MP_NOPTS_VALUE)
- MP_ERR(vf, "No PTS after filter!\n");
- p->got_frame = mp_image_new_copy(&img);
+ MP_ERR(vf, "No PTS after filter at frame %d!\n", n);
+ res = mp_image_new_copy(&img);
p->vsapi->freeFrame(f);
} else {
p->failed = true;
- MP_ERR(vf, "Filter error: %s\n", errorMsg);
+ MP_ERR(vf, "Filter error at frame %d: %s\n", n, errorMsg);
}
+ p->requested[index] = res;
pthread_cond_broadcast(&p->wakeup);
pthread_mutex_unlock(&p->lock);
}
@@ -222,8 +240,8 @@ static int filter_ext(struct vf_instance *vf, struct mp_image *mpi)
return 0;
// Turn PTS into frame duration (the pts field is abused for storing it)
- if (p->prev_pts == MP_NOPTS_VALUE)
- p->prev_pts = mpi->pts;
+ if (p->out_pts == MP_NOPTS_VALUE)
+ p->out_pts = mpi->pts;
mpi->pts = p->next_image ? p->next_image->pts - mpi->pts : 0;
// Try to get new frames until we get rid of the input mpi.
@@ -243,18 +261,30 @@ static int filter_ext(struct vf_instance *vf, struct mp_image *mpi)
pthread_cond_broadcast(&p->wakeup);
}
- if (p->got_frame) {
- vf_add_output_frame(vf, p->got_frame);
- p->got_frame = NULL;
+ if (p->requested[0] && p->requested[0] != &dummy_img) {
+ struct mp_image *out = p->requested[0];
+ if (out->pts != MP_NOPTS_VALUE) {
+ double duration = out->pts;
+ out->pts = p->out_pts;
+ p->out_pts += duration;
+ }
+ vf_add_output_frame(vf, out);
+ for (int n = 0; n < p->max_requests - 1; n++)
+ p->requested[n] = p->requested[n + 1];
+ p->requested[p->max_requests - 1] = NULL;
+ p->out_frameno++;
}
- if (!p->getting_frame) {
- // Note: this assumes getFrameAsync() will never call infiltGetFrame
- // (if it does, we would deadlock)
- p->getting_frame = true;
- p->failed = false;
- p->vsapi->getFrameAsync(p->out_frameno++, p->out_node,
- vs_frame_done, vf);
+ for (int n = 0; n < p->max_requests; n++) {
+ if (!p->requested[n]) {
+ // Note: this assumes getFrameAsync() will never call
+ // infiltGetFrame (if it does, we would deadlock)
+ p->requested[n] = (struct mp_image *)&dummy_img;
+ p->failed = false;
+ MP_DBG(vf, "requesting frame %d (%d)\n", p->out_frameno + n, n);
+ p->vsapi->getFrameAsync(p->out_frameno + n, p->out_node,
+ vs_frame_done, vf);
+ }
}
if (!mpi)
@@ -298,13 +328,20 @@ static const VSFrameRef *VS_CC infiltGetFrame(int frameno, int activationReason,
VSFrameRef *ret = NULL;
pthread_mutex_lock(&p->lock);
+ MP_DBG(vf, "VS requesting frame %d (at %d)\n", frameno, p->in_frameno);
while (1) {
- if (p->shutdown)
+ if (p->shutdown) {
+ p->vsapi->setFilterError("EOF or filter reinit/uninit\n", frameCtx);
break;
+ }
if (frameno < p->in_frameno) {
- p->vsapi->setFilterError("Requesting a frame too far in the past. "
- "Try increasing the maxbuffer suboption",
- frameCtx);
+ char msg[180];
+ snprintf(msg, sizeof(msg),
+ "Frame %d requested, but only have frames starting from %d.\n"
+ "Try increasing the buffered-frames suboption.\n",
+ frameno, p->in_frameno);
+ MP_FATAL(vf, "%s", msg);
+ p->vsapi->setFilterError(msg, frameCtx);
break;
}
if (frameno >= p->in_frameno + MP_TALLOC_ELEMS(p->buffered)) {
@@ -322,6 +359,7 @@ static const VSFrameRef *VS_CC infiltGetFrame(int frameno, int activationReason,
vsapi->getFormatPreset(mp_to_vs(img->imgfmt), core);
ret = vsapi->newVideoFrame(vsfmt, img->w, img->h, NULL, core);
if (!ret) {
+ assert(0);
p->vsapi->setFilterError("Could not allocate VS frame", frameCtx);
break;
}
@@ -358,11 +396,11 @@ static void destroy_vs(struct vf_instance *vf)
{
struct vf_priv_s *p = vf->priv;
- // Wait until our frame callback returns.
+ // Wait until our frame callbacks return.
pthread_mutex_lock(&p->lock);
p->shutdown = true;
pthread_cond_broadcast(&p->wakeup);
- while (p->getting_frame)
+ while (num_requested(p))
pthread_cond_wait(&p->wakeup, &p->lock);
pthread_mutex_unlock(&p->lock);
@@ -380,17 +418,19 @@ static void destroy_vs(struct vf_instance *vf)
p->vscore = NULL;
assert(!p->in_node_active);
+ assert(num_requested(p) == 0); // async callback didn't return?
p->shutdown = false;
- talloc_free(p->got_frame);
- p->got_frame = NULL;
+ // Kill filtered images that weren't returned yet
+ for (int n = 0; n < p->max_requests; n++)
+ mp_image_unrefp(&p->requested[n]);
// 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_pts = MP_NOPTS_VALUE;
p->out_frameno = p->in_frameno = 0;
}
@@ -531,14 +571,18 @@ static int vf_open(vf_instance_t *vf)
vf->query_format = query_format;
vf->control = control;
vf->uninit = uninit;
- p->buffered = talloc_array(vf, struct mp_image *, p->cfg_maxbuffer);
+ int maxbuffer = p->cfg_maxbuffer * p->cfg_maxrequests;
+ p->buffered = talloc_array(vf, struct mp_image *, maxbuffer);
+ p->max_requests = p->cfg_maxrequests;
+ p->requested = talloc_zero_array(vf, struct mp_image *, p->max_requests);
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)),
+ OPT_INTRANGE("buffered-frames", cfg_maxbuffer, 0, 1, 9999, OPTDEF_INT(4)),
+ OPT_INTRANGE("concurrent-frames", cfg_maxrequests, 0, 1, 99, OPTDEF_INT(2)),
{0}
};