From e44911142914783c9ec717f329bd9b6a8bb9b70e Mon Sep 17 00:00:00 2001 From: wm4 Date: Tue, 17 Dec 2013 00:53:22 +0100 Subject: Move mpvcore/player/ to player/ --- player/screenshot.c | 404 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 404 insertions(+) create mode 100644 player/screenshot.c (limited to 'player/screenshot.c') diff --git a/player/screenshot.c b/player/screenshot.c new file mode 100644 index 0000000000..7aaba5f05a --- /dev/null +++ b/player/screenshot.c @@ -0,0 +1,404 @@ +/* + * This file is part of mplayer2. + * + * mplayer2 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * mplayer2 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with mplayer2; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include +#include + +#include "config.h" + +#include "osdep/io.h" + +#include "talloc.h" +#include "screenshot.h" +#include "mp_core.h" +#include "command.h" +#include "mpvcore/bstr.h" +#include "mpvcore/mp_msg.h" +#include "mpvcore/path.h" +#include "video/mp_image.h" +#include "video/decode/dec_video.h" +#include "video/filter/vf.h" +#include "video/out/vo.h" +#include "video/image_writer.h" +#include "sub/osd.h" + +#include "video/csputils.h" + +#define MODE_FULL_WINDOW 1 +#define MODE_SUBTITLES 2 + +typedef struct screenshot_ctx { + struct MPContext *mpctx; + + int mode; + bool each_frame; + bool osd; + + int frameno; +} screenshot_ctx; + +void screenshot_init(struct MPContext *mpctx) +{ + mpctx->screenshot_ctx = talloc(mpctx, screenshot_ctx); + *mpctx->screenshot_ctx = (screenshot_ctx) { + .mpctx = mpctx, + .frameno = 1, + }; +} + +#define SMSG_OK 0 +#define SMSG_ERR 1 + +static void screenshot_msg(screenshot_ctx *ctx, int status, const char *msg, + ...) PRINTF_ATTRIBUTE(3,4); + +static void screenshot_msg(screenshot_ctx *ctx, int status, const char *msg, + ...) +{ + va_list ap; + char *s; + + va_start(ap, msg); + s = talloc_vasprintf(NULL, msg, ap); + va_end(ap); + + mp_msg(MSGT_CPLAYER, status == SMSG_ERR ? MSGL_ERR : MSGL_INFO, "%s\n", s); + if (ctx->osd) { + set_osd_msg(ctx->mpctx, OSD_MSG_TEXT, 1, ctx->mpctx->opts->osd_duration, + "%s", s); + } + + talloc_free(s); +} + +static char *stripext(void *talloc_ctx, const char *s) +{ + const char *end = strrchr(s, '.'); + if (!end) + end = s + strlen(s); + return talloc_asprintf(talloc_ctx, "%.*s", (int)(end - s), s); +} + +#ifdef _WIN32 +#define ILLEGAL_FILENAME_CHARS "?\"/\\<>*|:" +#else +#define ILLEGAL_FILENAME_CHARS "/" +#endif + +// Replace all characters disallowed in filenames with '_' and return the newly +// allocated result string. +static char *sanitize_filename(void *talloc_ctx, const char *s) +{ + char *res = talloc_strdup(talloc_ctx, s); + char *cur = res; + while (*cur) { + if (strchr(ILLEGAL_FILENAME_CHARS, *cur) || ((unsigned char)*cur) < 32) + *cur = '_'; + cur++; + } + return res; +} + +static void append_filename(char **s, const char *f) +{ + char *append = sanitize_filename(NULL, f); + *s = talloc_strdup_append(*s, append); + talloc_free(append); +} + +static char *create_fname(struct MPContext *mpctx, char *template, + const char *file_ext, int *sequence, int *frameno) +{ + char *res = talloc_strdup(NULL, ""); //empty string, non-NULL context + + time_t raw_time = time(NULL); + struct tm *local_time = localtime(&raw_time); + + if (!template || *template == '\0') + template = "shot%n"; + + for (;;) { + char *next = strchr(template, '%'); + if (!next) + break; + res = talloc_strndup_append(res, template, next - template); + template = next + 1; + char fmt = *template++; + switch (fmt) { + case '#': + case '0': + case 'n': { + int digits = '4'; + if (fmt == '#') { + if (!*sequence) { + *frameno = 1; + } + fmt = *template++; + } + if (fmt == '0') { + digits = *template++; + if (digits < '0' || digits > '9') + goto error_exit; + fmt = *template++; + } + if (fmt != 'n') + goto error_exit; + char fmtstr[] = {'%', '0', digits, 'd', '\0'}; + res = talloc_asprintf_append(res, fmtstr, *frameno); + if (*frameno < 100000 - 1) { + (*frameno) += 1; + (*sequence) += 1; + } + break; + } + case 'f': + case 'F': { + char *video_file = mp_basename(mpctx->filename); + if (video_file) { + char *name = video_file; + if (fmt == 'F') + name = stripext(res, video_file); + append_filename(&res, name); + } + break; + } + case 'p': + case 'P': { + char *t = mp_format_time(get_current_time(mpctx), fmt == 'P'); + append_filename(&res, t); + talloc_free(t); + break; + } + case 'w': { + char tfmt = *template; + if (!tfmt) + goto error_exit; + template++; + char fmtstr[] = {'%', tfmt, '\0'}; + char *s = mp_format_time_fmt(fmtstr, get_current_time(mpctx)); + if (!s) + goto error_exit; + append_filename(&res, s); + talloc_free(s); + break; + } + case 't': { + char tfmt = *template; + if (!tfmt) + goto error_exit; + template++; + char fmtstr[] = {'%', tfmt, '\0'}; + char buffer[80]; + if (strftime(buffer, sizeof(buffer), fmtstr, local_time) == 0) + buffer[0] = '\0'; + append_filename(&res, buffer); + break; + } + case '{': { + char *end = strchr(template, '}'); + if (!end) + goto error_exit; + struct bstr prop = bstr_splice(bstr0(template), 0, end - template); + char *tmp = talloc_asprintf(NULL, "${%.*s}", BSTR_P(prop)); + char *s = mp_property_expand_string(mpctx, tmp); + talloc_free(tmp); + if (s) + append_filename(&res, s); + talloc_free(s); + template = end + 1; + break; + } + case '%': + res = talloc_strdup_append(res, "%"); + break; + default: + goto error_exit; + } + } + + res = talloc_strdup_append(res, template); + res = talloc_asprintf_append(res, ".%s", file_ext); + char *fname = mp_get_user_path(NULL, res); + talloc_free(res); + return fname; + +error_exit: + talloc_free(res); + return NULL; +} + +static char *gen_fname(screenshot_ctx *ctx, const char *file_ext) +{ + int sequence = 0; + for (;;) { + int prev_sequence = sequence; + char *fname = create_fname(ctx->mpctx, + ctx->mpctx->opts->screenshot_template, + file_ext, + &sequence, + &ctx->frameno); + + if (!fname) { + screenshot_msg(ctx, SMSG_ERR, "Invalid screenshot filename " + "template! Fix or remove the --screenshot-template " + "option."); + return NULL; + } + + if (!mp_path_exists(fname)) + return fname; + + if (sequence == prev_sequence) { + screenshot_msg(ctx, SMSG_ERR, "Can't save screenshot, file '%s' " + "already exists!", fname); + talloc_free(fname); + return NULL; + } + + talloc_free(fname); + } +} + +static void add_subs(struct MPContext *mpctx, struct mp_image *image) +{ + int d_w = image->display_w ? image->display_w : image->w; + int d_h = image->display_h ? image->display_h : image->h; + + double sar = (double)image->w / image->h; + double dar = (double)d_w / d_h; + struct mp_osd_res res = { + .w = image->w, + .h = image->h, + .display_par = sar / dar, + }; + + osd_draw_on_image(mpctx->osd, res, mpctx->osd->vo_pts, + 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)) + 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; + if (mpctx->video_out && mpctx->video_out->config_ok) { + if (mode == MODE_SUBTITLES && mpctx->osd->render_subs_in_filter) + mode = 0; + + struct voctrl_screenshot_args args = + { .full_window = (mode == MODE_FULL_WINDOW) }; + + if (mpctx->d_video && mpctx->d_video->vfilter) + vf_control_any(mpctx->d_video->vfilter, VFCTRL_SCREENSHOT, &args); + + if (!args.out_image) + vo_control(mpctx->video_out, VOCTRL_SCREENSHOT, &args); + + image = args.out_image; + if (image) { + if (mode == MODE_SUBTITLES && !args.has_osd) + add_subs(mpctx, image); + } + } + 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) +{ + screenshot_ctx *ctx = mpctx->screenshot_ctx; + + if (!ctx->each_frame) + return; + + ctx->each_frame = false; + screenshot_request(mpctx, ctx->mode, true, ctx->osd); +} -- cgit v1.2.3