diff options
author | wm4 <wm4@nowhere> | 2017-04-01 20:45:20 +0200 |
---|---|---|
committer | wm4 <wm4@nowhere> | 2017-04-01 20:47:23 +0200 |
commit | 9bcb9fcf2652afce86c53b353d63cc3377862eab (patch) | |
tree | 0a0c978ae9f557a5a06a94508f077cefcf7a46ef /player/screenshot.c | |
parent | 6931fef4adfa96cef6a8dc67e89b4433aef72a36 (diff) | |
download | mpv-9bcb9fcf2652afce86c53b353d63cc3377862eab.tar.bz2 mpv-9bcb9fcf2652afce86c53b353d63cc3377862eab.tar.xz |
player: make screenshot commands honor the async flag
And also change input.conf to make all screenshots async. (Except the
every-frame mode, which always uses synchronous mode and ignores the
flag.) By default, the "screenshot" command is still asynchronous,
because scripts etc. might depend on this behavior.
This is only partially async. The code for determining the filename is
still always run synchronously. Only encoding the screenshot and writing
it to disk is asynchronous. We explicitly document the exact behavior as
undefined, so it can be changed any time.
Some of this is a bit messy, because I wanted to avoid duplicating the
message display code between sync and async mode. In async mode, this is
called from a worker thread, which is not safe because showing a message
accesses the thread-unsafe OSD code. So the core has to be locked during
this, which implies accessing the core and all that. So the code has
weird locking calls, and we need to do core destruction in a more
"controlled" manner (thus the outstanding_async field).
(What I'd really want would be the OSD simply showing log messages
instead.)
This is pretty untested, so expect bugs.
Fixes #4250.
Diffstat (limited to 'player/screenshot.c')
-rw-r--r-- | player/screenshot.c | 115 |
1 files changed, 91 insertions, 24 deletions
diff --git a/player/screenshot.c b/player/screenshot.c index b5bd5ea787..4c705556e4 100644 --- a/player/screenshot.c +++ b/player/screenshot.c @@ -28,6 +28,8 @@ #include "core.h" #include "command.h" #include "misc/bstr.h" +#include "misc/dispatch.h" +#include "misc/thread_pool.h" #include "common/msg.h" #include "options/path.h" #include "video/mp_image.h" @@ -50,6 +52,8 @@ typedef struct screenshot_ctx { bool osd; int frameno; + + struct mp_thread_pool *thread_pool; } screenshot_ctx; void screenshot_init(struct MPContext *mpctx) @@ -63,6 +67,7 @@ void screenshot_init(struct MPContext *mpctx) #define SMSG_OK 0 #define SMSG_ERR 1 +#define SMSG_V 2 static void screenshot_msg(screenshot_ctx *ctx, int status, const char *msg, ...) PRINTF_ATTRIBUTE(3,4); @@ -77,8 +82,14 @@ static void screenshot_msg(screenshot_ctx *ctx, int status, const char *msg, s = talloc_vasprintf(NULL, msg, ap); va_end(ap); - MP_MSG(ctx->mpctx, status == SMSG_ERR ? MSGL_ERR : MSGL_INFO, "%s\n", s); - if (ctx->osd) + int msg_level = MSGL_INFO; + if (status == SMSG_ERR) + msg_level = MSGL_ERR; + if (status == SMSG_V) + msg_level = MSGL_V; + + MP_MSG(ctx->mpctx, msg_level, "%s\n", s); + if (ctx->osd && msg_level <= MSGL_INFO) set_osd_msg(ctx->mpctx, 1, ctx->mpctx->opts->osd_duration, "%s", s); talloc_free(s); @@ -92,6 +103,75 @@ static char *stripext(void *talloc_ctx, const char *s) return talloc_asprintf(talloc_ctx, "%.*s", (int)(end - s), s); } +struct screenshot_item { + bool on_thread; + struct MPContext *mpctx; + const char *filename; + struct mp_image *img; + struct image_writer_opts opts; +}; + +#define LOCK(item) if (item->on_thread) mp_dispatch_lock(item->mpctx->dispatch); +#define UNLOCK(item) if (item->on_thread) mp_dispatch_unlock(item->mpctx->dispatch); + +static void write_screenshot_thread(void *arg) +{ + struct screenshot_item *item = arg; + screenshot_ctx *ctx = item->mpctx->screenshot_ctx; + + LOCK(item) + screenshot_msg(ctx, SMSG_OK, "Screenshot: '%s'", item->filename); + UNLOCK(item) + + if (!item->img || !write_image(item->img, &item->opts, item->filename, + item->mpctx->log)) + { + LOCK(item) + screenshot_msg(ctx, SMSG_ERR, "Error writing screenshot!"); + UNLOCK(item) + } + + if (item->on_thread) { + mp_dispatch_lock(item->mpctx->dispatch); + screenshot_msg(ctx, SMSG_V, "Screenshot writing done."); + item->mpctx->outstanding_async -= 1; + mp_wakeup_core(item->mpctx); + mp_dispatch_unlock(item->mpctx->dispatch); + } + + talloc_free(item); +} + +static void write_screenshot(struct MPContext *mpctx, struct mp_image *img, + const char *filename, struct image_writer_opts *opts, + bool async) +{ + screenshot_ctx *ctx = mpctx->screenshot_ctx; + struct image_writer_opts *gopts = mpctx->opts->screenshot_image_opts; + + struct screenshot_item *item = talloc_zero(NULL, struct screenshot_item); + *item = (struct screenshot_item){ + .mpctx = mpctx, + .filename = talloc_strdup(item, filename), + .img = talloc_steal(item, mp_image_new_ref(img)), + .opts = opts ? *opts : *gopts, + }; + + if (async) { + if (!ctx->thread_pool) + ctx->thread_pool = mp_thread_pool_create(ctx, 1); + if (ctx->thread_pool) { + item->on_thread = true; + mpctx->outstanding_async += 1; + mp_thread_pool_queue(ctx->thread_pool, write_screenshot_thread, item); + item = NULL; + } + } + + if (item) + write_screenshot_thread(item); +} + #ifdef _WIN32 #define ILLEGAL_FILENAME_CHARS "?\"/\\<>*|:" #else @@ -315,21 +395,6 @@ static void add_subs(struct MPContext *mpctx, struct mp_image *image) OSD_DRAW_SUB_ONLY, image); } -static void screenshot_save(struct MPContext *mpctx, struct mp_image *image) -{ - screenshot_ctx *ctx = mpctx->screenshot_ctx; - - struct image_writer_opts *opts = mpctx->opts->screenshot_image_opts; - - char *filename = gen_fname(ctx, image_writer_file_ext(opts)); - if (filename) { - screenshot_msg(ctx, SMSG_OK, "Screenshot: '%s'", filename); - if (!write_image(image, opts, filename, mpctx->log)) - screenshot_msg(ctx, SMSG_ERR, "Error writing screenshot!"); - talloc_free(filename); - } -} - static struct mp_image *screenshot_get(struct MPContext *mpctx, int mode) { struct mp_image *image = NULL; @@ -376,7 +441,7 @@ struct mp_image *screenshot_get_rgb(struct MPContext *mpctx, int mode) } void screenshot_to_file(struct MPContext *mpctx, const char *filename, int mode, - bool osd) + bool osd, bool async) { screenshot_ctx *ctx = mpctx->screenshot_ctx; struct image_writer_opts opts = *mpctx->opts->screenshot_image_opts; @@ -392,9 +457,7 @@ void screenshot_to_file(struct MPContext *mpctx, const char *filename, int mode, screenshot_msg(ctx, SMSG_ERR, "Taking screenshot failed."); goto end; } - screenshot_msg(ctx, SMSG_OK, "Screenshot: '%s'", filename); - if (!write_image(image, &opts, filename, mpctx->log)) - screenshot_msg(ctx, SMSG_ERR, "Error writing screenshot!"); + write_screenshot(mpctx, image, filename, &opts, async); talloc_free(image); end: @@ -402,7 +465,7 @@ end: } void screenshot_request(struct MPContext *mpctx, int mode, bool each_frame, - bool osd) + bool osd, bool async) { screenshot_ctx *ctx = mpctx->screenshot_ctx; @@ -423,7 +486,11 @@ void screenshot_request(struct MPContext *mpctx, int mode, bool each_frame, struct mp_image *image = screenshot_get(mpctx, mode); if (image) { - screenshot_save(mpctx, image); + struct image_writer_opts *opts = mpctx->opts->screenshot_image_opts; + char *filename = gen_fname(ctx, image_writer_file_ext(opts)); + if (filename) + write_screenshot(mpctx, image, filename, NULL, async); + talloc_free(filename); } else { screenshot_msg(ctx, SMSG_ERR, "Taking screenshot failed."); } @@ -439,5 +506,5 @@ void screenshot_flip(struct MPContext *mpctx) return; ctx->each_frame = false; - screenshot_request(mpctx, ctx->mode, true, ctx->osd); + screenshot_request(mpctx, ctx->mode, true, ctx->osd, false); } |