summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--DOCS/man/input.rst9
-rw-r--r--etc/input.conf6
-rw-r--r--player/command.c6
-rw-r--r--player/core.h6
-rw-r--r--player/main.c2
-rw-r--r--player/screenshot.c115
-rw-r--r--player/screenshot.h4
-rw-r--r--wscript_build.py1
8 files changed, 117 insertions, 32 deletions
diff --git a/DOCS/man/input.rst b/DOCS/man/input.rst
index babfd1aacd..4135b499ef 100644
--- a/DOCS/man/input.rst
+++ b/DOCS/man/input.rst
@@ -186,6 +186,12 @@ List of Input Commands
second argument (and did not have flags). This syntax is still understood,
but deprecated and might be removed in the future.
+ Setting the ``async`` flag will make encoding and writing the actual image
+ file asynchronous in most cases. (``each-frame`` mode ignores this flag
+ currently.) Requesting async screenshots too early or too often could lead
+ to the same filenames being chosen, and overwriting each others in undefined
+ order.
+
``screenshot-to-file "<filename>" [subtitles|video|window]``
Take a screenshot and save it to a given file. The format of the file will
be guessed by the extension (and ``--screenshot-format`` is ignored - the
@@ -198,6 +204,9 @@ List of Input Commands
Like all input command parameters, the filename is subject to property
expansion as described in `Property Expansion`_.
+ The ``async`` flag has an effect on this command (see ``screenshot``
+ command).
+
``playlist-next [weak|force]``
Go to the next entry on the playlist.
diff --git a/etc/input.conf b/etc/input.conf
index fe39b6c980..0e07f33e9e 100644
--- a/etc/input.conf
+++ b/etc/input.conf
@@ -119,9 +119,9 @@
#_ cycle video
#T cycle ontop # toggle video window ontop of other windows
#f cycle fullscreen # toggle fullscreen
-#s screenshot # take a screenshot
-#S screenshot video # ...without subtitles
-#Ctrl+s screenshot window # ...with subtitles and OSD, and scaled
+#s async screenshot # take a screenshot
+#S async screenshot video # ...without subtitles
+#Ctrl+s async screenshot window # ...with subtitles and OSD, and scaled
#Alt+s screenshot each-frame # automatically screenshot every frame
#w add panscan -0.1 # zoom out with -panscan 0 -fs
#e add panscan +0.1 # in
diff --git a/player/command.c b/player/command.c
index 900617c683..31cdb20015 100644
--- a/player/command.c
+++ b/player/command.c
@@ -4827,6 +4827,7 @@ int run_command(struct MPContext *mpctx, struct mp_cmd *cmd, struct mpv_node *re
bool bar_osd = auto_osd || (on_osd & MP_ON_OSD_BAR);
bool msg_or_nobar_osd = msg_osd && !(auto_osd && opts->osd_bar_visible);
int osdl = msg_osd ? 1 : OSD_LEVEL_INVISIBLE;
+ bool async = cmd->flags & MP_ASYNC_CMD;
mp_cmd_dump(mpctx->log, cmd->id == MP_CMD_IGNORE ? MSGL_DEBUG : MSGL_V,
"Run command:", cmd);
@@ -5366,12 +5367,13 @@ int run_command(struct MPContext *mpctx, struct mp_cmd *cmd, struct mpv_node *re
case MP_CMD_SCREENSHOT: {
int mode = cmd->args[0].v.i & 3;
int freq = (cmd->args[0].v.i | cmd->args[1].v.i) >> 3;
- screenshot_request(mpctx, mode, freq, msg_osd);
+ screenshot_request(mpctx, mode, freq, msg_osd, async);
break;
}
case MP_CMD_SCREENSHOT_TO_FILE:
- screenshot_to_file(mpctx, cmd->args[0].v.s, cmd->args[1].v.i, msg_osd);
+ screenshot_to_file(mpctx, cmd->args[0].v.s, cmd->args[1].v.i, msg_osd,
+ async);
break;
case MP_CMD_SCREENSHOT_RAW: {
diff --git a/player/core.h b/player/core.h
index 79120decd3..b958db56d5 100644
--- a/player/core.h
+++ b/player/core.h
@@ -239,6 +239,12 @@ typedef struct MPContext {
struct mp_dispatch_queue *dispatch;
struct mp_cancel *playback_abort;
bool in_dispatch;
+ // Number of asynchronous tasks that still need to finish until MPContext
+ // destruction is ok. It's implied that the async tasks call
+ // mp_wakeup_core() each time this is decremented.
+ // As using an atomic+wakeup would be racy, this is a normal integer, and
+ // mp_dispatch_lock must be called to change it.
+ int64_t outstanding_async;
struct mp_log *statusline;
struct osd_state *osd;
diff --git a/player/main.c b/player/main.c
index 42b56ea105..2045e3b40a 100644
--- a/player/main.c
+++ b/player/main.c
@@ -150,7 +150,7 @@ void mp_print_version(struct mp_log *log, int always)
static void shutdown_clients(struct MPContext *mpctx)
{
mp_client_enter_shutdown(mpctx);
- while (mp_clients_num(mpctx)) {
+ while (mp_clients_num(mpctx) || mpctx->outstanding_async) {
mp_client_broadcast_event(mpctx, MPV_EVENT_SHUTDOWN, NULL);
mp_wait_events(mpctx);
}
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);
}
diff --git a/player/screenshot.h b/player/screenshot.h
index aa5dfac2d0..69f6ac801d 100644
--- a/player/screenshot.h
+++ b/player/screenshot.h
@@ -31,13 +31,13 @@ void screenshot_init(struct MPContext *mpctx);
// screenshot slave command (MP_CMD_SCREENSHOT).
// osd: show status on OSD
void screenshot_request(struct MPContext *mpctx, int mode, bool each_frame,
- bool osd);
+ bool osd, bool async);
// filename: where to store the screenshot; doesn't try to find an alternate
// name if the file already exists
// mode, osd: same as in screenshot_request()
void screenshot_to_file(struct MPContext *mpctx, const char *filename, int mode,
- bool osd);
+ bool osd, bool async);
// mode is the same as in screenshot_request()
struct mp_image *screenshot_get_rgb(struct MPContext *mpctx, int mode);
diff --git a/wscript_build.py b/wscript_build.py
index 8155018a12..9be4a59fbf 100644
--- a/wscript_build.py
+++ b/wscript_build.py
@@ -215,6 +215,7 @@ def build(ctx):
( "misc/node.c" ),
( "misc/ring.c" ),
( "misc/rendezvous.c" ),
+ ( "misc/thread_pool.c" ),
## Options
( "options/m_config.c" ),