mirror of https://github.com/mpv-player/mpv
command: add screenshot_to_file command
This commit is contained in:
parent
0bb41524f0
commit
c460258f5a
|
@ -140,6 +140,19 @@ List of Input Commands
|
||||||
Take a screenshot each frame. Issue this command again to stop taking
|
Take a screenshot each frame. Issue this command again to stop taking
|
||||||
screenshots.
|
screenshots.
|
||||||
|
|
||||||
|
``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
|
||||||
|
behavior when the extension is missing or unknown is arbitrary).
|
||||||
|
|
||||||
|
The second argument is like the first argument to ``screenshot``.
|
||||||
|
|
||||||
|
This command tries to never overwrite files. If the file already exists,
|
||||||
|
it fails.
|
||||||
|
|
||||||
|
Like all input command parameters, the filename is subject to property
|
||||||
|
expansion as described in `Property Expansion`_.
|
||||||
|
|
||||||
``playlist_next [weak|force]``
|
``playlist_next [weak|force]``
|
||||||
Go to the next entry on the playlist.
|
Go to the next entry on the playlist.
|
||||||
|
|
||||||
|
|
|
@ -2420,6 +2420,10 @@ void run_command(MPContext *mpctx, mp_cmd_t *cmd)
|
||||||
screenshot_request(mpctx, cmd->args[0].v.i, cmd->args[1].v.i, msg_osd);
|
screenshot_request(mpctx, cmd->args[0].v.i, cmd->args[1].v.i, msg_osd);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case MP_CMD_SCREENSHOT_TO_FILE:
|
||||||
|
screenshot_to_file(mpctx, cmd->args[0].v.s, cmd->args[1].v.i, msg_osd);
|
||||||
|
break;
|
||||||
|
|
||||||
case MP_CMD_RUN:
|
case MP_CMD_RUN:
|
||||||
#ifndef __MINGW32__
|
#ifndef __MINGW32__
|
||||||
if (!fork()) {
|
if (!fork()) {
|
||||||
|
|
|
@ -171,6 +171,12 @@ static const mp_cmd_t mp_cmds[] = {
|
||||||
OARG_CHOICE(0, ({"single", 0},
|
OARG_CHOICE(0, ({"single", 0},
|
||||||
{"each-frame", 1})),
|
{"each-frame", 1})),
|
||||||
}},
|
}},
|
||||||
|
{ MP_CMD_SCREENSHOT_TO_FILE, "screenshot_to_file", {
|
||||||
|
ARG_STRING,
|
||||||
|
OARG_CHOICE(2, ({"video", 0},
|
||||||
|
{"window", 1},
|
||||||
|
{"subtitles", 2})),
|
||||||
|
}},
|
||||||
{ MP_CMD_LOADFILE, "loadfile", {
|
{ MP_CMD_LOADFILE, "loadfile", {
|
||||||
ARG_STRING,
|
ARG_STRING,
|
||||||
OARG_CHOICE(0, ({"replace", 0}, {"0", 0},
|
OARG_CHOICE(0, ({"replace", 0}, {"0", 0},
|
||||||
|
|
|
@ -36,6 +36,7 @@ enum mp_command_type {
|
||||||
MP_CMD_TV_STEP_NORM,
|
MP_CMD_TV_STEP_NORM,
|
||||||
MP_CMD_TV_STEP_CHANNEL_LIST,
|
MP_CMD_TV_STEP_CHANNEL_LIST,
|
||||||
MP_CMD_SCREENSHOT,
|
MP_CMD_SCREENSHOT,
|
||||||
|
MP_CMD_SCREENSHOT_TO_FILE,
|
||||||
MP_CMD_LOADFILE,
|
MP_CMD_LOADFILE,
|
||||||
MP_CMD_LOADLIST,
|
MP_CMD_LOADLIST,
|
||||||
MP_CMD_PLAYLIST_CLEAR,
|
MP_CMD_PLAYLIST_CLEAR,
|
||||||
|
|
11
core/path.c
11
core/path.c
|
@ -164,6 +164,17 @@ struct bstr mp_dirname(const char *path)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
char *mp_splitext(const char *path, bstr *root)
|
||||||
|
{
|
||||||
|
assert(path);
|
||||||
|
const char *split = strrchr(path, '.');
|
||||||
|
if (!split)
|
||||||
|
split = path + strlen(path);
|
||||||
|
if (root)
|
||||||
|
*root = (bstr){.start = (char *)path, .len = path - split};
|
||||||
|
return (char *)split;
|
||||||
|
}
|
||||||
|
|
||||||
char *mp_path_join(void *talloc_ctx, struct bstr p1, struct bstr p2)
|
char *mp_path_join(void *talloc_ctx, struct bstr p1, struct bstr p2)
|
||||||
{
|
{
|
||||||
if (p1.len == 0)
|
if (p1.len == 0)
|
||||||
|
|
|
@ -40,6 +40,13 @@ char *mp_find_user_config_file(const char *filename);
|
||||||
|
|
||||||
char *mp_basename(const char *path);
|
char *mp_basename(const char *path);
|
||||||
|
|
||||||
|
/* Return file extension, including the '.'. If root is not NULL, set it to the
|
||||||
|
* part of the path without extension. So: path == root + returnvalue
|
||||||
|
* Don't consider it a file extension if the only '.' is the first character.
|
||||||
|
* Return "" if no extension.
|
||||||
|
*/
|
||||||
|
char *mp_splitext(const char *path, bstr *root);
|
||||||
|
|
||||||
/* Return struct bstr referencing directory part of path, or if that
|
/* Return struct bstr referencing directory part of path, or if that
|
||||||
* would be empty, ".".
|
* would be empty, ".".
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -278,16 +278,12 @@ static void add_subs(struct MPContext *mpctx, struct mp_image *image)
|
||||||
OSD_DRAW_SUB_ONLY, image);
|
OSD_DRAW_SUB_ONLY, image);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void screenshot_save(struct MPContext *mpctx, struct mp_image *image,
|
static void screenshot_save(struct MPContext *mpctx, struct mp_image *image)
|
||||||
bool with_subs)
|
|
||||||
{
|
{
|
||||||
screenshot_ctx *ctx = mpctx->screenshot_ctx;
|
screenshot_ctx *ctx = mpctx->screenshot_ctx;
|
||||||
|
|
||||||
struct image_writer_opts *opts = mpctx->opts.screenshot_image_opts;
|
struct image_writer_opts *opts = mpctx->opts.screenshot_image_opts;
|
||||||
|
|
||||||
if (with_subs)
|
|
||||||
add_subs(mpctx, image);
|
|
||||||
|
|
||||||
char *filename = gen_fname(ctx, image_writer_file_ext(opts));
|
char *filename = gen_fname(ctx, image_writer_file_ext(opts));
|
||||||
if (filename) {
|
if (filename) {
|
||||||
screenshot_msg(ctx, SMSG_OK, "Screenshot: '%s'", filename);
|
screenshot_msg(ctx, SMSG_OK, "Screenshot: '%s'", filename);
|
||||||
|
@ -295,30 +291,15 @@ static void screenshot_save(struct MPContext *mpctx, struct mp_image *image,
|
||||||
screenshot_msg(ctx, SMSG_ERR, "Error writing screenshot!");
|
screenshot_msg(ctx, SMSG_ERR, "Error writing screenshot!");
|
||||||
talloc_free(filename);
|
talloc_free(filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
talloc_free(image);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void screenshot_request(struct MPContext *mpctx, int mode, bool each_frame,
|
static struct mp_image *screenshot_get(struct MPContext *mpctx, int mode)
|
||||||
bool osd)
|
|
||||||
{
|
{
|
||||||
|
struct mp_image *image = NULL;
|
||||||
if (mpctx->video_out && mpctx->video_out->config_ok) {
|
if (mpctx->video_out && mpctx->video_out->config_ok) {
|
||||||
screenshot_ctx *ctx = mpctx->screenshot_ctx;
|
|
||||||
|
|
||||||
if (mode == MODE_SUBTITLES && mpctx->osd->render_subs_in_filter)
|
if (mode == MODE_SUBTITLES && mpctx->osd->render_subs_in_filter)
|
||||||
mode = 0;
|
mode = 0;
|
||||||
|
|
||||||
if (each_frame) {
|
|
||||||
ctx->each_frame = !ctx->each_frame;
|
|
||||||
if (!ctx->each_frame)
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
ctx->each_frame = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx->mode = mode;
|
|
||||||
ctx->osd = osd;
|
|
||||||
|
|
||||||
struct voctrl_screenshot_args args =
|
struct voctrl_screenshot_args args =
|
||||||
{ .full_window = (mode == MODE_FULL_WINDOW) };
|
{ .full_window = (mode == MODE_FULL_WINDOW) };
|
||||||
|
|
||||||
|
@ -328,14 +309,73 @@ void screenshot_request(struct MPContext *mpctx, int mode, bool each_frame,
|
||||||
if (!args.out_image)
|
if (!args.out_image)
|
||||||
vo_control(mpctx->video_out, VOCTRL_SCREENSHOT, &args);
|
vo_control(mpctx->video_out, VOCTRL_SCREENSHOT, &args);
|
||||||
|
|
||||||
if (args.out_image) {
|
image = args.out_image;
|
||||||
if (args.has_osd)
|
if (image) {
|
||||||
mode = 0;
|
if (mode == MODE_SUBTITLES && !args.has_osd)
|
||||||
screenshot_save(mpctx, args.out_image, mode == MODE_SUBTITLES);
|
add_subs(mpctx, image);
|
||||||
} else {
|
|
||||||
screenshot_msg(ctx, SMSG_ERR, "Taking screenshot failed.");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return image;
|
||||||
|
}
|
||||||
|
|
||||||
|
void screenshot_to_file(struct MPContext *mpctx, const char *filename, int mode,
|
||||||
|
bool osd)
|
||||||
|
{
|
||||||
|
screenshot_ctx *ctx = mpctx->screenshot_ctx;
|
||||||
|
struct image_writer_opts opts = *mpctx->opts.screenshot_image_opts;
|
||||||
|
bool old_osd = ctx->osd;
|
||||||
|
ctx->osd = osd;
|
||||||
|
|
||||||
|
if (mp_path_exists(filename)) {
|
||||||
|
screenshot_msg(ctx, SMSG_ERR, "Screenshot: file '%s' already exists.",
|
||||||
|
filename);
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
char *ext = mp_splitext(filename, NULL);
|
||||||
|
if (ext)
|
||||||
|
opts.format = ext + 1; // omit '.'
|
||||||
|
struct mp_image *image = screenshot_get(mpctx, mode);
|
||||||
|
if (!image) {
|
||||||
|
screenshot_msg(ctx, SMSG_ERR, "Taking screenshot failed.");
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
screenshot_msg(ctx, SMSG_OK, "Screenshot: '%s'", filename);
|
||||||
|
if (!write_image(image, &opts, filename))
|
||||||
|
screenshot_msg(ctx, SMSG_ERR, "Error writing screenshot!");
|
||||||
|
talloc_free(image);
|
||||||
|
|
||||||
|
end:
|
||||||
|
ctx->osd = old_osd;
|
||||||
|
}
|
||||||
|
|
||||||
|
void screenshot_request(struct MPContext *mpctx, int mode, bool each_frame,
|
||||||
|
bool osd)
|
||||||
|
{
|
||||||
|
screenshot_ctx *ctx = mpctx->screenshot_ctx;
|
||||||
|
|
||||||
|
if (mode == MODE_SUBTITLES && mpctx->osd->render_subs_in_filter)
|
||||||
|
mode = 0;
|
||||||
|
|
||||||
|
if (each_frame) {
|
||||||
|
ctx->each_frame = !ctx->each_frame;
|
||||||
|
if (!ctx->each_frame)
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
ctx->each_frame = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx->mode = mode;
|
||||||
|
ctx->osd = osd;
|
||||||
|
|
||||||
|
struct mp_image *image = screenshot_get(mpctx, mode);
|
||||||
|
|
||||||
|
if (image) {
|
||||||
|
screenshot_save(mpctx, image);
|
||||||
|
} else {
|
||||||
|
screenshot_msg(ctx, SMSG_ERR, "Taking screenshot failed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
talloc_free(image);
|
||||||
}
|
}
|
||||||
|
|
||||||
void screenshot_flip(struct MPContext *mpctx)
|
void screenshot_flip(struct MPContext *mpctx)
|
||||||
|
|
|
@ -34,6 +34,12 @@ void screenshot_init(struct MPContext *mpctx);
|
||||||
void screenshot_request(struct MPContext *mpctx, int mode, bool each_frame,
|
void screenshot_request(struct MPContext *mpctx, int mode, bool each_frame,
|
||||||
bool osd);
|
bool osd);
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
|
||||||
// Called by the playback core code when a new frame is displayed.
|
// Called by the playback core code when a new frame is displayed.
|
||||||
void screenshot_flip(struct MPContext *mpctx);
|
void screenshot_flip(struct MPContext *mpctx);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue