summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorwm4 <wm4@mplayer2.org>2011-10-06 20:46:01 +0200
committerUoti Urpala <uau@mplayer2.org>2011-11-25 23:56:28 +0200
commit01cf896a2f0d8be92c6d6633095fa2719a8c0e58 (patch)
tree674e4a00ffc236651bc1035fb411541cc55efede
parente3f5043233336d8b4b0731c6a8b42a8fda5535ac (diff)
downloadmpv-01cf896a2f0d8be92c6d6633095fa2719a8c0e58.tar.bz2
mpv-01cf896a2f0d8be92c6d6633095fa2719a8c0e58.tar.xz
core: add infrastructure to get screenshots from VOs
Add a VO command (VOCTRL_SCREENSHOT) which requests a screenshot directly from the VO. If VO support is available, screenshots will be taken instantly (no more 1 or 2 frames delay). Taking screenshots when hardware decoding is in use will also work (vdpau). Additionally, the screenshots will now use the same colorspace as the video display. Change the central MPContext to be allocated with talloc so that it can be used as a talloc parent context. This commit does not yet implement the functionality for any VO (added in subsequent commits). The old screenshot video filter is not needed anymore if VO support is present, and in that case will not be used even if it is present in the filter chain. If VO support is not available then the filter is used like before. Note that the filter still has some of the old problems, such as delaying the screenshot by at least 1 frame.
-rw-r--r--Makefile1
-rw-r--r--command.c11
-rw-r--r--libmpcodecs/vf.h8
-rw-r--r--libmpcodecs/vf_screenshot.c181
-rw-r--r--libvo/video_out.h12
-rw-r--r--mp_core.h2
-rw-r--r--mplayer.c7
-rw-r--r--screenshot.c230
-rw-r--r--screenshot.h39
9 files changed, 347 insertions, 144 deletions
diff --git a/Makefile b/Makefile
index 6013ca3c88..1f7422c34c 100644
--- a/Makefile
+++ b/Makefile
@@ -504,6 +504,7 @@ SRCS_MPLAYER = command.c \
mp_fifo.c \
mplayer.c \
parser-mpcmd.c \
+ screenshot.c \
input/input.c \
libao2/ao_mpegpes.c \
libao2/ao_null.c \
diff --git a/command.c b/command.c
index bdb9343187..3b77deaa9e 100644
--- a/command.c
+++ b/command.c
@@ -66,6 +66,7 @@
#endif
#include "stream/stream_dvdnav.h"
#include "m_struct.h"
+#include "screenshot.h"
#include "mp_core.h"
#include "mp_fifo.h"
@@ -3380,15 +3381,7 @@ void run_command(MPContext *mpctx, mp_cmd_t *cmd)
break;
case MP_CMD_SCREENSHOT:
- if (mpctx->video_out && mpctx->video_out->config_ok) {
- mp_msg(MSGT_CPLAYER, MSGL_INFO, "sending VFCTRL_SCREENSHOT!\n");
- if (CONTROL_OK !=
- ((vf_instance_t *) sh_video->vfilter)->
- control(sh_video->vfilter, VFCTRL_SCREENSHOT,
- &cmd->args[0].v.i))
- mp_msg(MSGT_CPLAYER, MSGL_INFO,
- "failed (forgot -vf screenshot?)\n");
- }
+ screenshot_request(mpctx, cmd->args[0].v.i);
break;
case MP_CMD_VF_CHANGE_RECTANGLE:
diff --git a/libmpcodecs/vf.h b/libmpcodecs/vf.h
index 11ee5eaf8e..e1df715551 100644
--- a/libmpcodecs/vf.h
+++ b/libmpcodecs/vf.h
@@ -95,6 +95,12 @@ typedef struct vf_seteq_s
int value;
} vf_equalizer_t;
+struct vf_ctrl_screenshot {
+ // When the screenshot is complete, pass it to this callback.
+ void (*image_callback)(void *, mp_image_t *);
+ void *image_callback_ctx;
+};
+
#define VFCTRL_QUERY_MAX_PP_LEVEL 4 /* test for postprocessing support (max level) */
#define VFCTRL_SET_PP_LEVEL 5 /* set postprocessing level */
#define VFCTRL_SET_EQUALIZER 6 /* set color options (brightness,contrast etc) */
@@ -104,7 +110,7 @@ typedef struct vf_seteq_s
#define VFCTRL_DUPLICATE_FRAME 11 /* For encoding - encode zero-change frame */
#define VFCTRL_SKIP_NEXT_FRAME 12 /* For encoding - drop the next frame that passes thru */
#define VFCTRL_FLUSH_FRAMES 13 /* For encoding - flush delayed frames */
-#define VFCTRL_SCREENSHOT 14 /* Make a screenshot */
+#define VFCTRL_SCREENSHOT 14 // Take screenshot, arg is vf_ctrl_screenshot
#define VFCTRL_INIT_EOSD 15 /* Select EOSD renderer */
#define VFCTRL_DRAW_EOSD 16 /* Render EOSD */
#define VFCTRL_SET_DEINTERLACE 18 /* Set deinterlacing status */
diff --git a/libmpcodecs/vf_screenshot.c b/libmpcodecs/vf_screenshot.c
index 04f0d6e5d9..7f9a45c879 100644
--- a/libmpcodecs/vf_screenshot.c
+++ b/libmpcodecs/vf_screenshot.c
@@ -23,35 +23,21 @@
#include <string.h>
#include <inttypes.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <unistd.h>
-
-#include <libswscale/swscale.h>
-#include <libavcodec/avcodec.h>
-
#include "mp_msg.h"
#include "img_format.h"
#include "mp_image.h"
#include "vf.h"
#include "vf_scale.h"
#include "fmt-conversion.h"
+#include "libvo/fastmemcpy.h"
+#include <libswscale/swscale.h>
struct vf_priv_s {
- int frameno;
- char fname[102];
- /// shot stores current screenshot mode:
- /// 0: don't take screenshots
- /// 1: take single screenshot, reset to 0 afterwards
- /// 2: take screenshots of each frame
+ mp_image_t *image;
+ void (*image_callback)(void *, mp_image_t *);
+ void *image_callback_ctx;
int shot, store_slices;
- int dw, dh, stride;
- uint8_t *buffer;
- struct SwsContext *ctx;
- AVCodecContext *avctx;
- uint8_t *outbuffer;
- int outbuffer_size;
};
//===========================================================================//
@@ -60,102 +46,51 @@ static int config(struct vf_instance *vf,
int width, int height, int d_width, int d_height,
unsigned int flags, unsigned int outfmt)
{
- vf->priv->ctx=sws_getContextFromCmdLine(width, height, outfmt,
- d_width, d_height, IMGFMT_RGB24);
-
- vf->priv->outbuffer_size = d_width * d_height * 3 * 2;
- vf->priv->outbuffer = realloc(vf->priv->outbuffer, vf->priv->outbuffer_size);
- vf->priv->avctx->width = d_width;
- vf->priv->avctx->height = d_height;
- vf->priv->avctx->pix_fmt = PIX_FMT_RGB24;
- vf->priv->avctx->compression_level = 0;
- vf->priv->dw = d_width;
- vf->priv->dh = d_height;
- vf->priv->stride = (3*vf->priv->dw+15)&~15;
-
- free(vf->priv->buffer); // probably reconfigured
- vf->priv->buffer = NULL;
-
+ free_mp_image(vf->priv->image);
+ vf->priv->image = new_mp_image(width, height);
+ mp_image_setfmt(vf->priv->image, outfmt);
+ vf->priv->image->w = d_width;
+ vf->priv->image->h = d_height;
return vf_next_config(vf,width,height,d_width,d_height,flags,outfmt);
}
-static void write_png(struct vf_priv_s *priv)
-{
- char *fname = priv->fname;
- FILE * fp;
- AVFrame pic;
- int size;
-
- fp = fopen (fname, "wb");
- if (fp == NULL) {
- mp_msg(MSGT_VFILTER,MSGL_ERR,"\nPNG Error opening %s for writing!\n", fname);
- return;
- }
-
- pic.data[0] = priv->buffer;
- pic.linesize[0] = priv->stride;
- size = avcodec_encode_video(priv->avctx, priv->outbuffer, priv->outbuffer_size, &pic);
- if (size > 0)
- fwrite(priv->outbuffer, size, 1, fp);
-
- fclose (fp);
-}
-
-static int fexists(char *fname)
-{
- struct stat dummy;
- if (stat(fname, &dummy) == 0) return 1;
- else return 0;
-}
-
-static void gen_fname(struct vf_priv_s* priv)
-{
- do {
- snprintf (priv->fname, 100, "shot%04d.png", ++priv->frameno);
- } while (fexists(priv->fname) && priv->frameno < 100000);
- if (fexists(priv->fname)) {
- priv->fname[0] = '\0';
- return;
- }
-
- mp_msg(MSGT_VFILTER,MSGL_INFO,"*** screenshot '%s' ***\n",priv->fname);
-
-}
-
-static void scale_image(struct vf_priv_s* priv, mp_image_t *mpi)
-{
- uint8_t *dst[MP_MAX_PLANES] = {NULL};
- int dst_stride[MP_MAX_PLANES] = {0};
-
- dst_stride[0] = priv->stride;
- if (!priv->buffer)
- priv->buffer = av_malloc(dst_stride[0]*priv->dh);
-
- dst[0] = priv->buffer;
- sws_scale(priv->ctx, (const uint8_t **)mpi->planes, mpi->stride, 0, priv->dh, dst, dst_stride);
-}
-
static void start_slice(struct vf_instance *vf, mp_image_t *mpi)
{
vf->dmpi=vf_get_image(vf->next,mpi->imgfmt,
mpi->type, mpi->flags, mpi->width, mpi->height);
if (vf->priv->shot) {
vf->priv->store_slices = 1;
- if (!vf->priv->buffer)
- vf->priv->buffer = av_malloc(vf->priv->stride*vf->priv->dh);
+ if (!(vf->priv->image->flags & MP_IMGFLAG_ALLOCATED))
+ mp_image_alloc_planes(vf->priv->image);
}
}
+static void memcpy_pic_slice(unsigned char *dst, unsigned char *src,
+ int bytesPerLine, int y, int h,
+ int dstStride, int srcStride)
+{
+ memcpy_pic(dst + h * dstStride, src + h * srcStride, bytesPerLine,
+ h, dstStride, srcStride);
+}
+
static void draw_slice(struct vf_instance *vf, unsigned char** src,
int* stride, int w,int h, int x, int y)
{
if (vf->priv->store_slices) {
- uint8_t *dst[MP_MAX_PLANES] = {NULL};
- int dst_stride[MP_MAX_PLANES] = {0};
- dst_stride[0] = vf->priv->stride;
- dst[0] = vf->priv->buffer;
- sws_scale(vf->priv->ctx, (const uint8_t **)src, stride, y, h, dst, dst_stride);
+ mp_image_t *dst = vf->priv->image;
+ int bp = (dst->bpp + 7) / 8;
+
+ if (dst->flags & MP_IMGFLAG_PLANAR) {
+ int bytes_per_line[3] = { w * bp, dst->chroma_width, dst->chroma_width };
+ for (int n = 0; n < 3; n++) {
+ memcpy_pic_slice(dst->planes[n], src[n], bytes_per_line[n],
+ y, h, dst->stride[n], stride[n]);
+ }
+ } else {
+ memcpy_pic_slice(dst->planes[0], src[0], dst->w*bp, y, dst->h,
+ dst->stride[0], stride[0]);
+ }
}
vf_next_draw_slice(vf,src,stride,w,h,x,y);
}
@@ -200,14 +135,15 @@ static int put_image(struct vf_instance *vf, mp_image_t *mpi, double pts)
}
if(vf->priv->shot) {
- if (vf->priv->shot==1)
- vf->priv->shot=0;
- gen_fname(vf->priv);
- if (vf->priv->fname[0]) {
- if (!vf->priv->store_slices)
- scale_image(vf->priv, dmpi);
- write_png(vf->priv);
- }
+ vf->priv->shot=0;
+ mp_image_t image;
+ if (!vf->priv->store_slices)
+ image = *dmpi;
+ else
+ image = *vf->priv->image;
+ image.w = vf->priv->image->w;
+ image.h = vf->priv->image->h;
+ vf->priv->image_callback(vf->priv->image_callback_ctx, &image);
vf->priv->store_slices = 0;
}
@@ -216,20 +152,11 @@ static int put_image(struct vf_instance *vf, mp_image_t *mpi, double pts)
static int control (vf_instance_t *vf, int request, void *data)
{
- /** data contains an integer argument
- * 0: take screenshot with the next frame
- * 1: take screenshots with each frame until the same command is given once again
- **/
if(request==VFCTRL_SCREENSHOT) {
- if (data && *(int*)data) { // repeated screenshot mode
- if (vf->priv->shot==2)
- vf->priv->shot=0;
- else
- vf->priv->shot=2;
- } else { // single screenshot
- if (!vf->priv->shot)
- vf->priv->shot=1;
- }
+ struct vf_ctrl_screenshot *cmd = (struct vf_ctrl_screenshot *)data;
+ vf->priv->image_callback = cmd->image_callback;
+ vf->priv->image_callback_ctx = cmd->image_callback_ctx;
+ vf->priv->shot=1;
return CONTROL_TRUE;
}
return vf_next_control (vf, request, data);
@@ -249,11 +176,7 @@ static int query_format(struct vf_instance *vf, unsigned int fmt)
static void uninit(vf_instance_t *vf)
{
- avcodec_close(vf->priv->avctx);
- av_freep(&vf->priv->avctx);
- if(vf->priv->ctx) sws_freeContext(vf->priv->ctx);
- av_free(vf->priv->buffer);
- free(vf->priv->outbuffer);
+ free_mp_image(vf->priv->image);
free(vf->priv);
}
@@ -268,17 +191,9 @@ static int vf_open(vf_instance_t *vf, char *args)
vf->get_image=get_image;
vf->uninit=uninit;
vf->priv=malloc(sizeof(struct vf_priv_s));
- vf->priv->frameno=0;
vf->priv->shot=0;
vf->priv->store_slices=0;
- vf->priv->buffer=0;
- vf->priv->outbuffer=0;
- vf->priv->ctx=0;
- vf->priv->avctx = avcodec_alloc_context();
- if (avcodec_open(vf->priv->avctx, avcodec_find_encoder(CODEC_ID_PNG))) {
- mp_msg(MSGT_VFILTER, MSGL_FATAL, "Could not open libavcodec PNG encoder\n");
- return 0;
- }
+ vf->priv->image=NULL;
return 1;
}
diff --git a/libvo/video_out.h b/libvo/video_out.h
index d1a2a7f65a..fd61d9c1a1 100644
--- a/libvo/video_out.h
+++ b/libvo/video_out.h
@@ -78,6 +78,8 @@ enum mp_voctrl {
VOCTRL_SET_YUV_COLORSPACE, // struct mp_csp_details
VOCTRL_GET_YUV_COLORSPACE, // struct mp_csp_details
+
+ VOCTRL_SCREENSHOT, // struct voctrl_screenshot_args
};
// VOCTRL_SET_EQUALIZER
@@ -104,6 +106,16 @@ typedef struct mp_eosd_res {
int mt, mb, ml, mr; // borders (top, bottom, left, right)
} mp_eosd_res_t;
+// VOCTRL_SCREENSHOT
+struct voctrl_screenshot_args {
+ // Will be set to a newly allocated image, that contains the screenshot.
+ // The caller has to free the pointer with free_mp_image().
+ // It is not specified whether the image data is a copy or references the
+ // image data directly.
+ // Is never NULL. (Failure has to be indicated by returning VO_FALSE.)
+ struct mp_image *out_image;
+};
+
typedef struct {
int x,y;
int w,h;
diff --git a/mp_core.h b/mp_core.h
index e467ce9c08..8b36465c5f 100644
--- a/mp_core.h
+++ b/mp_core.h
@@ -216,6 +216,8 @@ typedef struct MPContext {
// playback rate. Used to avoid showing it multiple times.
bool drop_message_shown;
+ struct screenshot_ctx *screenshot_ctx;
+
#ifdef CONFIG_DVDNAV
struct mp_image *nav_smpi; ///< last decoded dvdnav video image
unsigned char *nav_buffer; ///< last read dvdnav video frame
diff --git a/mplayer.c b/mplayer.c
index 7030a50da0..70ad4d04e0 100644
--- a/mplayer.c
+++ b/mplayer.c
@@ -73,6 +73,7 @@
#include "mp_osd.h"
#include "libvo/video_out.h"
+#include "screenshot.h"
#include "sub/font_load.h"
#include "sub/sub.h"
@@ -791,6 +792,8 @@ void exit_player_with_rc(struct MPContext *mpctx, enum exit_reason how, int rc)
m_config_free(mpctx->mconfig);
mpctx->mconfig = NULL;
+ talloc_free(mpctx);
+
exit(rc);
}
@@ -3711,6 +3714,7 @@ static void run_playloop(struct MPContext *mpctx)
get_relative_time(mpctx);
}
print_status(mpctx, MP_NOPTS_VALUE, true);
+ screenshot_flip(mpctx);
} else
print_status(mpctx, MP_NOPTS_VALUE, false);
@@ -3943,7 +3947,8 @@ int main(int argc, char *argv[])
int opt_exit = 0;
int i;
- struct MPContext *mpctx = &(struct MPContext){
+ struct MPContext *mpctx = talloc(NULL, MPContext);
+ *mpctx = (struct MPContext){
.osd_function = OSD_PLAY,
.begin_skip = MP_NOPTS_VALUE,
.play_tree_step = 1,
diff --git a/screenshot.c b/screenshot.c
new file mode 100644
index 0000000000..e05bbfe480
--- /dev/null
+++ b/screenshot.c
@@ -0,0 +1,230 @@
+/*
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <inttypes.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <libswscale/swscale.h>
+#include <libavcodec/avcodec.h>
+
+#include "config.h"
+#include "talloc.h"
+#include "screenshot.h"
+#include "mp_core.h"
+#include "mp_msg.h"
+#include "libmpcodecs/img_format.h"
+#include "libmpcodecs/mp_image.h"
+#include "libmpcodecs/dec_video.h"
+#include "libmpcodecs/vf.h"
+#include "libvo/video_out.h"
+
+#include "fmt-conversion.h"
+
+//for sws_getContextFromCmdLine and mp_sws_set_colorspace
+#include "libmpcodecs/vf_scale.h"
+#include "libvo/csputils.h"
+
+typedef struct screenshot_ctx {
+ int each_frame;
+ int using_vf_screenshot;
+
+ int frameno;
+ char fname[102];
+} screenshot_ctx;
+
+static screenshot_ctx *screenshot_get_ctx(MPContext *mpctx)
+{
+ if (!mpctx->screenshot_ctx)
+ mpctx->screenshot_ctx = talloc_zero(mpctx, screenshot_ctx);
+ return mpctx->screenshot_ctx;
+}
+
+static int write_png(screenshot_ctx *ctx, struct mp_image *image)
+{
+ char *fname = ctx->fname;
+ FILE *fp = NULL;
+ void *outbuffer = NULL;
+ int success = 0;
+
+ AVCodecContext *avctx = avcodec_alloc_context();
+ if (!avctx)
+ goto error_exit;
+
+ if (avcodec_open(avctx, avcodec_find_encoder(CODEC_ID_PNG))) {
+ mp_msg(MSGT_CPLAYER, MSGL_INFO, "Could not open libavcodec PNG encoder"
+ " for saving screenshot\n");
+ goto error_exit;
+ }
+
+ avctx->width = image->width;
+ avctx->height = image->height;
+ avctx->pix_fmt = PIX_FMT_RGB24;
+ avctx->compression_level = 0;
+
+ size_t outbuffer_size = image->width * image->height * 3 * 2;
+ outbuffer = malloc(outbuffer_size);
+ if (!outbuffer)
+ goto error_exit;
+
+ AVFrame pic;
+ pic.data[0] = image->planes[0];
+ pic.linesize[0] = image->stride[0];
+ int size = avcodec_encode_video(avctx, outbuffer, outbuffer_size, &pic);
+ if (size < 1)
+ goto error_exit;
+
+ fp = fopen(fname, "wb");
+ if (fp == NULL) {
+ avcodec_close(avctx);
+ mp_msg(MSGT_CPLAYER, MSGL_ERR, "\nPNG Error opening %s for writing!\n",
+ fname);
+ goto error_exit;
+ }
+
+ fwrite(outbuffer, size, 1, fp);
+ fflush(fp);
+
+ if (ferror(fp))
+ goto error_exit;
+
+ success = 1;
+error_exit:
+ if (avctx)
+ avcodec_close(avctx);
+ if (fp)
+ fclose(fp);
+ free(outbuffer);
+ return success;
+}
+
+static int fexists(char *fname)
+{
+ struct stat dummy;
+ if (stat(fname, &dummy) == 0)
+ return 1;
+ else
+ return 0;
+}
+
+static void gen_fname(screenshot_ctx *ctx)
+{
+ do {
+ snprintf(ctx->fname, 100, "shot%04d.png", ++ctx->frameno);
+ } while (fexists(ctx->fname) && ctx->frameno < 100000);
+ if (fexists(ctx->fname)) {
+ ctx->fname[0] = '\0';
+ return;
+ }
+
+ mp_msg(MSGT_CPLAYER, MSGL_INFO, "*** screenshot '%s' ***\n", ctx->fname);
+
+}
+
+void screenshot_save(struct MPContext *mpctx, struct mp_image *image)
+{
+ screenshot_ctx *ctx = screenshot_get_ctx(mpctx);
+ struct mp_image *dst = alloc_mpi(image->w, image->h, IMGFMT_RGB24);
+
+ struct SwsContext *sws = sws_getContextFromCmdLine(image->width,
+ image->height,
+ image->imgfmt,
+ dst->width,
+ dst->height,
+ dst->imgfmt);
+
+ struct mp_csp_details colorspace;
+ get_detected_video_colorspace(mpctx->sh_video, &colorspace);
+ // this is a property of the output device; images always use full-range RGB
+ colorspace.levels_out = MP_CSP_LEVELS_PC;
+ mp_sws_set_colorspace(sws, &colorspace);
+
+ sws_scale(sws, (const uint8_t **)image->planes, image->stride, 0,
+ image->height, dst->planes, dst->stride);
+
+ gen_fname(ctx);
+ write_png(ctx, dst);
+
+ sws_freeContext(sws);
+ free_mp_image(dst);
+}
+
+static void vf_screenshot_callback(void *pctx, struct mp_image *image)
+{
+ struct MPContext *mpctx = (struct MPContext *)pctx;
+ screenshot_ctx *ctx = screenshot_get_ctx(mpctx);
+ screenshot_save(mpctx, image);
+ if (ctx->each_frame)
+ screenshot_request(mpctx, 0);
+}
+
+void screenshot_request(struct MPContext *mpctx, bool each_frame)
+{
+ if (mpctx->video_out && mpctx->video_out->config_ok) {
+ screenshot_ctx *ctx = screenshot_get_ctx(mpctx);
+
+ ctx->using_vf_screenshot = 0;
+
+ if (each_frame) {
+ ctx->each_frame = !ctx->each_frame;
+ if (!ctx->each_frame)
+ return;
+ }
+
+ struct voctrl_screenshot_args args;
+ if (vo_control(mpctx->video_out, VOCTRL_SCREENSHOT, &args) == true) {
+ screenshot_save(mpctx, args.out_image);
+ free_mp_image(args.out_image);
+ } else {
+ mp_msg(MSGT_CPLAYER, MSGL_INFO, "No VO support for taking"
+ " screenshots, trying VFCTRL_SCREENSHOT!\n");
+ ctx->using_vf_screenshot = 1;
+ struct vf_ctrl_screenshot cmd = {
+ .image_callback = vf_screenshot_callback,
+ .image_callback_ctx = mpctx,
+ };
+ struct vf_instance *vfilter = mpctx->sh_video->vfilter;
+ if (vfilter->control(vfilter, VFCTRL_SCREENSHOT, &cmd) !=
+ CONTROL_OK)
+ mp_msg(MSGT_CPLAYER, MSGL_INFO,
+ "...failed (need --vf=screenshot?)\n");
+ }
+ }
+}
+
+void screenshot_flip(struct MPContext *mpctx)
+{
+ screenshot_ctx *ctx = screenshot_get_ctx(mpctx);
+
+ if (!ctx->each_frame)
+ return;
+
+ // screenshot_flip is called when the VO presents a new frame. vf_screenshot
+ // can behave completely different (consider filters inserted between
+ // vf_screenshot and vf_vo, that add or remove frames), so handle this case
+ // somewhere else.
+ if (ctx->using_vf_screenshot)
+ return;
+
+ screenshot_request(mpctx, 0);
+}
diff --git a/screenshot.h b/screenshot.h
new file mode 100644
index 0000000000..207d58e003
--- /dev/null
+++ b/screenshot.h
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+
+#ifndef MPLAYER_SCREENSHOT_H
+#define MPLAYER_SCREENSHOT_H
+
+#include <stdbool.h>
+
+struct MPContext;
+struct mp_image;
+
+// Request a taking & saving a screenshot of the currently displayed frame.
+// If each_frame is set, this toggles per-frame screenshots, exactly like the
+// screenshot slave command (MP_CMD_SCREENSHOT).
+void screenshot_request(struct MPContext *mpctx, bool each_frame);
+
+// Save the screenshot contained in the image to disk.
+// The image can be in any format supported by libswscale.
+void screenshot_save(struct MPContext *mpctx, struct mp_image *image);
+
+// Called by the playback core code when a new frame is displayed.
+void screenshot_flip(struct MPContext *mpctx);
+
+#endif /* MPLAYER_SCREENSHOT_H */