From 01cf896a2f0d8be92c6d6633095fa2719a8c0e58 Mon Sep 17 00:00:00 2001 From: wm4 Date: Thu, 6 Oct 2011 20:46:01 +0200 Subject: 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. --- Makefile | 1 + command.c | 11 +-- libmpcodecs/vf.h | 8 +- libmpcodecs/vf_screenshot.c | 181 +++++++++------------------------- libvo/video_out.h | 12 +++ mp_core.h | 2 + mplayer.c | 7 +- screenshot.c | 230 ++++++++++++++++++++++++++++++++++++++++++++ screenshot.h | 39 ++++++++ 9 files changed, 347 insertions(+), 144 deletions(-) create mode 100644 screenshot.c create mode 100644 screenshot.h 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 #include -#include -#include -#include - -#include -#include - #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 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 +#include +#include +#include + +#include +#include +#include + +#include +#include + +#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 + +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 */ -- cgit v1.2.3 From 3041ee8d6c97fbccfe891edad5b57143fb1c6c2a Mon Sep 17 00:00:00 2001 From: wm4 Date: Thu, 6 Oct 2011 20:46:01 +0200 Subject: core: add screenshot mode for actual VO window contents The screenshot command normally converts the currently displayed video frame to an image. Add support for an alternative screenshot mode that is supposed to capture the real window contents. Such a screenshot contains a possibly scaled version of the frame, the OSD, and subtitles. Add a default key binding Alt+s for taking screenshots in this mode. This needs special VO support, and might not work with all VOs (this commit does not yet contain an implementation for any VO, only the infrastructure). --- DOCS/tech/slave.txt | 11 +++++++++-- command.c | 2 +- input/input.c | 4 +++- libvo/video_out.h | 7 +++++++ screenshot.c | 11 +++++++---- screenshot.h | 8 +++++--- 6 files changed, 32 insertions(+), 11 deletions(-) diff --git a/DOCS/tech/slave.txt b/DOCS/tech/slave.txt index 3b249f75d1..6bb1dc53c4 100644 --- a/DOCS/tech/slave.txt +++ b/DOCS/tech/slave.txt @@ -177,10 +177,17 @@ get_video_codec get_video_resolution Print out the video resolution of the current file. -screenshot +screenshot Take a screenshot. Requires the screenshot filter to be loaded. - 0 Take a single screenshot. + each_frame: + 0 Take a single screenshot. (Default.) 1 Start/stop taking screenshot of each frame. + full_window: + 0 Save the video image, in its original resolution. Typically without + OSD or subtitles. (Default.) + 1 Save the contents of the mplayer window. Typically with OSD and + subtitles. If not available (no VO support), this may act as if 0 was + passed. key_down_event Inject key code event into MPlayer. diff --git a/command.c b/command.c index 3b77deaa9e..71370e7033 100644 --- a/command.c +++ b/command.c @@ -3381,7 +3381,7 @@ void run_command(MPContext *mpctx, mp_cmd_t *cmd) break; case MP_CMD_SCREENSHOT: - screenshot_request(mpctx, cmd->args[0].v.i); + screenshot_request(mpctx, cmd->args[0].v.i, cmd->args[1].v.i); break; case MP_CMD_VF_CHANGE_RECTANGLE: diff --git a/input/input.c b/input/input.c index 7c02be5fa9..e1c001077a 100644 --- a/input/input.c +++ b/input/input.c @@ -176,7 +176,7 @@ static const mp_cmd_t mp_cmds[] = { { MP_CMD_VO_ONTOP, "vo_ontop", 0, { {MP_CMD_ARG_INT,{-1}}, {-1,{0}} } }, { MP_CMD_VO_ROOTWIN, "vo_rootwin", 0, { {MP_CMD_ARG_INT,{-1}}, {-1,{0}} } }, { MP_CMD_VO_BORDER, "vo_border", 0, { {MP_CMD_ARG_INT,{-1}}, {-1,{0}} } }, - { MP_CMD_SCREENSHOT, "screenshot", 0, { {MP_CMD_ARG_INT,{0}}, {-1,{0}} } }, + { MP_CMD_SCREENSHOT, "screenshot", 0, { {MP_CMD_ARG_INT,{0}}, {MP_CMD_ARG_INT,{0}}, {-1,{0}} } }, { MP_CMD_PANSCAN, "panscan",1, { {MP_CMD_ARG_FLOAT,{0}}, {MP_CMD_ARG_INT,{0}}, {-1,{0}} } }, { MP_CMD_SWITCH_VSYNC, "switch_vsync", 0, { {MP_CMD_ARG_INT,{0}}, {-1,{0}} } }, { MP_CMD_LOADFILE, "loadfile", 1, { {MP_CMD_ARG_STRING, {0}}, {MP_CMD_ARG_INT,{0}}, {-1,{0}} } }, @@ -507,6 +507,8 @@ static const struct cmd_bind def_cmd_binds[] = { { { 'C', 0 }, "step_property_osd capturing" }, { { 's', 0 }, "screenshot 0" }, { { 'S', 0 }, "screenshot 1" }, + { { KEY_MODIFIER_ALT + 's', 0 }, "screenshot 0 1" }, + { { KEY_MODIFIER_ALT + 'S', 0 }, "screenshot 1 1" }, { { 'w', 0 }, "panscan -0.1" }, { { 'e', 0 }, "panscan +0.1" }, diff --git a/libvo/video_out.h b/libvo/video_out.h index fd61d9c1a1..bd7b2e8fa5 100644 --- a/libvo/video_out.h +++ b/libvo/video_out.h @@ -108,6 +108,13 @@ typedef struct mp_eosd_res { // VOCTRL_SCREENSHOT struct voctrl_screenshot_args { + // 0: Save image of the currently displayed video frame, in original + // resolution. + // 1: Save full screenshot of the window. Should contain OSD, EOSD, and the + // scaled video. + // The value of this variable can be ignored if only a single method is + // implemented. + int full_window; // 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 diff --git a/screenshot.c b/screenshot.c index e05bbfe480..9b8c67a89a 100644 --- a/screenshot.c +++ b/screenshot.c @@ -46,6 +46,7 @@ #include "libvo/csputils.h" typedef struct screenshot_ctx { + int full_window; int each_frame; int using_vf_screenshot; @@ -175,10 +176,11 @@ static void vf_screenshot_callback(void *pctx, struct mp_image *image) screenshot_ctx *ctx = screenshot_get_ctx(mpctx); screenshot_save(mpctx, image); if (ctx->each_frame) - screenshot_request(mpctx, 0); + screenshot_request(mpctx, 0, ctx->full_window); } -void screenshot_request(struct MPContext *mpctx, bool each_frame) +void screenshot_request(struct MPContext *mpctx, bool each_frame, + bool full_window) { if (mpctx->video_out && mpctx->video_out->config_ok) { screenshot_ctx *ctx = screenshot_get_ctx(mpctx); @@ -187,11 +189,12 @@ void screenshot_request(struct MPContext *mpctx, bool each_frame) if (each_frame) { ctx->each_frame = !ctx->each_frame; + ctx->full_window = full_window; if (!ctx->each_frame) return; } - struct voctrl_screenshot_args args; + struct voctrl_screenshot_args args = { .full_window = full_window }; if (vo_control(mpctx->video_out, VOCTRL_SCREENSHOT, &args) == true) { screenshot_save(mpctx, args.out_image); free_mp_image(args.out_image); @@ -226,5 +229,5 @@ void screenshot_flip(struct MPContext *mpctx) if (ctx->using_vf_screenshot) return; - screenshot_request(mpctx, 0); + screenshot_request(mpctx, 0, ctx->full_window); } diff --git a/screenshot.h b/screenshot.h index 207d58e003..c57778c0b3 100644 --- a/screenshot.h +++ b/screenshot.h @@ -25,9 +25,11 @@ 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); +// each_frame: If set, this toggles per-frame screenshots, exactly like the +// screenshot slave command (MP_CMD_SCREENSHOT). +// full_window: If set, save the actual output window contents. +void screenshot_request(struct MPContext *mpctx, bool each_frame, + bool full_window); // Save the screenshot contained in the image to disk. // The image can be in any format supported by libswscale. -- cgit v1.2.3 From a661bd8f0da6b0d37afeae9dce4a5988c5f13b76 Mon Sep 17 00:00:00 2001 From: wm4 Date: Thu, 6 Oct 2011 20:46:01 +0200 Subject: video: use talloc for mp_image, abort if out of memory Make new_mp_image() allocate the struct with talloc and abort() if the av_malloc for image plane data fails. --- libmpcodecs/mp_image.c | 34 ++++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/libmpcodecs/mp_image.c b/libmpcodecs/mp_image.c index 88702dde60..4683f41174 100644 --- a/libmpcodecs/mp_image.c +++ b/libmpcodecs/mp_image.c @@ -22,9 +22,7 @@ #include #include -#if HAVE_MALLOC_H -#include -#endif +#include "talloc.h" #include "libmpcodecs/img_format.h" #include "libmpcodecs/mp_image.h" @@ -39,6 +37,8 @@ void mp_image_alloc_planes(mp_image_t *mpi) { mpi->chroma_width*mpi->chroma_height); } else mpi->planes[0]=av_malloc(mpi->bpp*mpi->width*(mpi->height+2)/8); + if (!mpi->planes[0]) + abort(); //out of memory if (mpi->flags&MP_IMGFLAG_PLANAR) { int bpp = IMGFMT_IS_YUVP16(mpi->imgfmt)? 2 : 1; // YV12/I420/YVU9/IF09. feel free to add other planar formats here... @@ -188,23 +188,29 @@ void mp_image_setfmt(mp_image_t* mpi,unsigned int out_fmt){ mpi->bpp=0; } +static int mp_image_destructor(void *ptr) +{ + mp_image_t *mpi = ptr; + + if(mpi->flags&MP_IMGFLAG_ALLOCATED){ + /* because we allocate the whole image at once */ + av_free(mpi->planes[0]); + if (mpi->flags & MP_IMGFLAG_RGB_PALETTE) + av_free(mpi->planes[1]); + } + + return 0; +} + mp_image_t* new_mp_image(int w,int h){ - mp_image_t* mpi = malloc(sizeof(mp_image_t)); - if(!mpi) return NULL; // error! - memset(mpi,0,sizeof(mp_image_t)); + mp_image_t* mpi = talloc_zero(NULL, mp_image_t); + talloc_set_destructor(mpi, mp_image_destructor); mpi->width=mpi->w=w; mpi->height=mpi->h=h; return mpi; } void free_mp_image(mp_image_t* mpi){ - if(!mpi) return; - if(mpi->flags&MP_IMGFLAG_ALLOCATED){ - /* becouse we allocate the whole image in once */ - av_free(mpi->planes[0]); - if (mpi->flags & MP_IMGFLAG_RGB_PALETTE) - av_free(mpi->planes[1]); - } - free(mpi); + talloc_free(mpi); } -- cgit v1.2.3 From 0440460d0cb1a65cc890508279b287c97db38a78 Mon Sep 17 00:00:00 2001 From: wm4 Date: Thu, 6 Oct 2011 20:46:01 +0200 Subject: vo_vdpau: implement screenshots --- TOOLS/vdpau_functions.py | 1 + libvo/vdpau_template.c | 1 + libvo/vo_vdpau.c | 100 +++++++++++++++++++++++++++++++++++++++++------ 3 files changed, 91 insertions(+), 11 deletions(-) diff --git a/TOOLS/vdpau_functions.py b/TOOLS/vdpau_functions.py index 098e30a251..27d6ff45f3 100644 --- a/TOOLS/vdpau_functions.py +++ b/TOOLS/vdpau_functions.py @@ -16,6 +16,7 @@ device_destroy generate_csc_matrix GenerateCSCMatrix # CSC completely capitalized output_surface_create output_surface_destroy +output_surface_get_bits_native output_surface_put_bits_indexed output_surface_put_bits_native output_surface_render_bitmap_surface diff --git a/libvo/vdpau_template.c b/libvo/vdpau_template.c index 3f9b26c5d8..14e5e7d211 100644 --- a/libvo/vdpau_template.c +++ b/libvo/vdpau_template.c @@ -18,6 +18,7 @@ VDP_FUNCTION(VdpDeviceDestroy, VDP_FUNC_ID_DEVICE_DESTROY, device_destroy) VDP_FUNCTION(VdpGenerateCSCMatrix, VDP_FUNC_ID_GENERATE_CSC_MATRIX, generate_csc_matrix) VDP_FUNCTION(VdpOutputSurfaceCreate, VDP_FUNC_ID_OUTPUT_SURFACE_CREATE, output_surface_create) VDP_FUNCTION(VdpOutputSurfaceDestroy, VDP_FUNC_ID_OUTPUT_SURFACE_DESTROY, output_surface_destroy) +VDP_FUNCTION(VdpOutputSurfaceGetBitsNative, VDP_FUNC_ID_OUTPUT_SURFACE_GET_BITS_NATIVE, output_surface_get_bits_native) VDP_FUNCTION(VdpOutputSurfacePutBitsIndexed, VDP_FUNC_ID_OUTPUT_SURFACE_PUT_BITS_INDEXED, output_surface_put_bits_indexed) VDP_FUNCTION(VdpOutputSurfacePutBitsNative, VDP_FUNC_ID_OUTPUT_SURFACE_PUT_BITS_NATIVE, output_surface_put_bits_native) VDP_FUNCTION(VdpOutputSurfaceRenderBitmapSurface, VDP_FUNC_ID_OUTPUT_SURFACE_RENDER_BITMAP_SURFACE, output_surface_render_bitmap_surface) diff --git a/libvo/vo_vdpau.c b/libvo/vo_vdpau.c index 322645ebd3..be4540002b 100644 --- a/libvo/vo_vdpau.c +++ b/libvo/vo_vdpau.c @@ -88,6 +88,9 @@ /* Initial size of EOSD surface in pixels (x*x) */ #define EOSD_SURFACE_INITIAL_SIZE 256 +/* Pixelformat used for output surfaces */ +#define OUTPUT_RGBA_FORMAT VDP_RGBA_FORMAT_B8G8R8A8 + /* * Global variable declaration - VDPAU specific */ @@ -113,7 +116,7 @@ struct vdpctx { uint64_t last_vdp_time; unsigned int last_sync_update; - /* an extra last output surface is misused for OSD. */ + /* an extra last output surface is used for OSD and screenshots */ VdpOutputSurface output_surfaces[MAX_OUTPUT_SURFACES + 1]; int num_output_surfaces; struct buffered_video_surface { @@ -158,6 +161,7 @@ struct vdpctx { bool dropped_frame; uint64_t dropped_time; uint32_t vid_width, vid_height; + uint32_t vid_d_width, vid_d_height; uint32_t image_format; VdpChromaType vdp_chroma_type; VdpYCbCrFormat vdp_pixel_format; @@ -242,7 +246,9 @@ static uint64_t convert_to_vdptime(struct vo *vo, unsigned int t) static void flip_page_timed(struct vo *vo, unsigned int pts_us, int duration); -static int video_to_output_surface(struct vo *vo) +static int render_video_to_output_surface(struct vo *vo, + VdpOutputSurface output_surface, + VdpRect *output_rect) { struct vdpctx *vc = vo->priv; struct vdp_functions *vdp = vc->vdp; @@ -265,7 +271,6 @@ static int video_to_output_surface(struct vo *vo) bv[(dp+1)/2].surface, bv[(dp+2)/2].surface}; const VdpVideoSurface *future_fields = (const VdpVideoSurface []){ dp >= 1 ? bv[(dp-1)/2].surface : VDP_INVALID_HANDLE}; - VdpOutputSurface output_surface = vc->output_surfaces[vc->surface_num]; vdp_st = vdp->presentation_queue_block_until_surface_idle(vc->flip_queue, output_surface, &dummy); @@ -276,11 +281,20 @@ static int video_to_output_surface(struct vo *vo) 0, field, 2, past_fields, bv[dp/2].surface, 1, future_fields, &vc->src_rect_vid, output_surface, - NULL, &vc->out_rect_vid, 0, NULL); + NULL, output_rect, 0, NULL); CHECK_ST_WARNING("Error when calling vdp_video_mixer_render"); return 0; } +static int video_to_output_surface(struct vo *vo) +{ + struct vdpctx *vc = vo->priv; + + return render_video_to_output_surface(vo, + vc->output_surfaces[vc->surface_num], + &vc->out_rect_vid); +} + static void get_buffered_frame(struct vo *vo, bool eof) { struct vdpctx *vc = vo->priv; @@ -393,18 +407,21 @@ static void resize(struct vo *vo) int flip_offset_ms = vo_fs ? vc->flip_offset_fs : vc->flip_offset_window; vo->flip_queue_offset = flip_offset_ms / 1000.; + int min_output_width = FFMAX(vo->dwidth, vc->vid_width); + int min_output_height = FFMAX(vo->dheight, vc->vid_height); + bool had_frames = vc->num_shown_frames; - if (vc->output_surface_width < vo->dwidth - || vc->output_surface_height < vo->dheight) { - if (vc->output_surface_width < vo->dwidth) { + if (vc->output_surface_width < min_output_width + || vc->output_surface_height < min_output_height) { + if (vc->output_surface_width < min_output_width) { vc->output_surface_width += vc->output_surface_width >> 1; vc->output_surface_width = FFMAX(vc->output_surface_width, - vo->dwidth); + min_output_width); } - if (vc->output_surface_height < vo->dheight) { + if (vc->output_surface_height < min_output_height) { vc->output_surface_height += vc->output_surface_height >> 1; vc->output_surface_height = FFMAX(vc->output_surface_height, - vo->dheight); + min_output_height); } // Creation of output_surfaces for (i = 0; i <= vc->num_output_surfaces; i++) { @@ -414,7 +431,7 @@ static void resize(struct vo *vo) "vdp_output_surface_destroy"); } vdp_st = vdp->output_surface_create(vc->vdp_device, - VDP_RGBA_FORMAT_B8G8R8A8, + OUTPUT_RGBA_FORMAT, vc->output_surface_width, vc->output_surface_height, &vc->output_surfaces[i]); @@ -868,6 +885,9 @@ static int config(struct vo *vo, uint32_t width, uint32_t height, vc->image_format = format; vc->vid_width = width; vc->vid_height = height; + vc->vid_d_width = d_width; + vc->vid_d_height = d_height; + free_video_specific(vo); if (IMGFMT_IS_VDPAU(vc->image_format) && !create_vdp_decoder(vo, 2)) return -1; @@ -1475,6 +1495,56 @@ static void draw_image(struct vo *vo, mp_image_t *mpi, double pts) return; } +// warning: the size and pixel format of surface must match that of the +// surfaces in vc->output_surfaces +static struct mp_image *read_output_surface(struct vdpctx *vc, + VdpOutputSurface surface) +{ + VdpStatus vdp_st; + struct vdp_functions *vdp = vc->vdp; + struct mp_image *image = alloc_mpi(vc->output_surface_width, + vc->output_surface_height, IMGFMT_BGR32); + + void *dst_planes[] = { image->planes[0] }; + uint32_t dst_pitches[] = { image->stride[0] }; + vdp_st = vdp->output_surface_get_bits_native(surface, NULL, dst_planes, + dst_pitches); + CHECK_ST_WARNING("Error when calling vdp_output_surface_get_bits_native"); + + return image; +} + +static struct mp_image *get_screenshot(struct vo *vo) +{ + struct vdpctx *vc = vo->priv; + + VdpOutputSurface screenshot_surface = + vc->output_surfaces[vc->num_output_surfaces]; + + VdpRect rc = { .x1 = vc->vid_width, .y1 = vc->vid_height }; + render_video_to_output_surface(vo, screenshot_surface, &rc); + + struct mp_image *image = read_output_surface(vc, screenshot_surface); + + image->width = vc->vid_width; + image->height = vc->vid_height; + image->w = vc->vid_d_width; + image->h = vc->vid_d_height; + + return image; +} + +static struct mp_image *get_window_screenshot(struct vo *vo) +{ + struct vdpctx *vc = vo->priv; + int last_surface = WRAP_ADD(vc->surface_num, -1, vc->num_output_surfaces); + VdpOutputSurface screen = vc->output_surfaces[last_surface]; + struct mp_image *image = read_output_surface(vo->priv, screen); + image->width = image->w = vo->dwidth; + image->height = image->h = vo->dheight; + return image; +} + static uint32_t get_image(struct vo *vo, mp_image_t *mpi) { struct vdpctx *vc = vo->priv; @@ -1797,6 +1867,14 @@ static int control(struct vo *vo, uint32_t request, void *data) case VOCTRL_RESET: forget_frames(vo); return true; + case VOCTRL_SCREENSHOT: { + struct voctrl_screenshot_args *args = data; + if (args->full_window) + args->out_image = get_window_screenshot(vo); + else + args->out_image = get_screenshot(vo); + return true; + } } return VO_NOTIMPL; } -- cgit v1.2.3 From 28eaf11cf3b220871b97d9ba40d5058a84bcba09 Mon Sep 17 00:00:00 2001 From: wm4 Date: Thu, 6 Oct 2011 20:46:01 +0200 Subject: vo_gl: implement screenshots --- libvo/gl_common.c | 19 ++++++++++++++++++ libvo/gl_common.h | 2 ++ libvo/vo_gl.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 80 insertions(+) diff --git a/libvo/gl_common.c b/libvo/gl_common.c index bbcbeb2f3b..f433f5642d 100644 --- a/libvo/gl_common.c +++ b/libvo/gl_common.c @@ -71,6 +71,7 @@ void glAdjustAlignment(GL *gl, int stride) else gl_alignment = 1; gl->PixelStorei(GL_UNPACK_ALIGNMENT, gl_alignment); + gl->PixelStorei(GL_PACK_ALIGNMENT, gl_alignment); } struct gl_name_map_struct { @@ -634,6 +635,24 @@ void glUploadTex(GL *gl, GLenum target, GLenum format, GLenum type, gl->TexSubImage2D(target, 0, x, y, w, y_max - y, format, type, data); } +/** + * \brief download a texture, handling things like stride and slices + * \param target texture target, usually GL_TEXTURE_2D + * \param format OpenGL format of data + * \param type OpenGL type of data + * \param dataptr destination memory for download + * \param stride data stride (must be positive) + * \ingroup gltexture + */ +void glDownloadTex(GL *gl, GLenum target, GLenum format, GLenum type, + void *dataptr, int stride) +{ + // this is not always correct, but should work for MPlayer + glAdjustAlignment(gl, stride); + gl->PixelStorei(GL_PACK_ROW_LENGTH, stride / glFmt2bpp(format, type)); + gl->GetTexImage(target, 0, format, type, dataptr); +} + /** * \brief Setup ATI version of register combiners for YUV to RGB conversion. * \param csp_params parameters used for colorspace conversion diff --git a/libvo/gl_common.h b/libvo/gl_common.h index a56248403a..d1d034b99a 100644 --- a/libvo/gl_common.h +++ b/libvo/gl_common.h @@ -256,6 +256,8 @@ int glCreatePPMTex(GL *gl, GLenum target, GLenum fmt, GLint filter, void glUploadTex(GL *gl, GLenum target, GLenum format, GLenum type, const void *dataptr, int stride, int x, int y, int w, int h, int slice); +void glDownloadTex(GL *gl, GLenum target, GLenum format, GLenum type, + void *dataptr, int stride); void glDrawTex(GL *gl, GLfloat x, GLfloat y, GLfloat w, GLfloat h, GLfloat tx, GLfloat ty, GLfloat tw, GLfloat th, int sx, int sy, int rect_tex, int is_yv12, int flip); diff --git a/libvo/vo_gl.c b/libvo/vo_gl.c index 5f06cf7148..e99ff4432f 100644 --- a/libvo/vo_gl.c +++ b/libvo/vo_gl.c @@ -1134,6 +1134,57 @@ skip_upload: return VO_TRUE; } +static mp_image_t *get_screenshot(struct vo *vo) +{ + struct gl_priv *p = vo->priv; + GL *gl = p->gl; + + mp_image_t *image = alloc_mpi(p->texture_width, p->texture_height, + p->image_format); + + glDownloadTex(gl, p->target, p->gl_format, p->gl_type, image->planes[0], + image->stride[0]); + + if (p->is_yuv) { + gl->ActiveTexture(GL_TEXTURE1); + glDownloadTex(gl, p->target, p->gl_format, p->gl_type, image->planes[1], + image->stride[1]); + gl->ActiveTexture(GL_TEXTURE2); + glDownloadTex(gl, p->target, p->gl_format, p->gl_type, image->planes[2], + image->stride[2]); + gl->ActiveTexture(GL_TEXTURE0); + } + + image->width = p->image_width; + image->height = p->image_height; + + image->w = p->image_d_width; + image->h = p->image_d_height; + + return image; +} + +static mp_image_t *get_window_screenshot(struct vo *vo) +{ + struct gl_priv *p = vo->priv; + GL *gl = p->gl; + + GLint vp[4]; //x, y, w, h + gl->GetIntegerv(GL_VIEWPORT, vp); + mp_image_t *image = alloc_mpi(vp[2], vp[3], IMGFMT_RGB24); + gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); + gl->PixelStorei(GL_PACK_ALIGNMENT, 0); + gl->PixelStorei(GL_PACK_ROW_LENGTH, 0); + gl->ReadBuffer(GL_FRONT); + //flip image while reading + for (int y = 0; y < vp[3]; y++) { + gl->ReadPixels(vp[0], vp[1] + vp[3] - y - 1, vp[2], 1, + GL_RGB, GL_UNSIGNED_BYTE, + image->planes[0] + y * image->stride[0]); + } + return image; +} + static int query_format(struct vo *vo, uint32_t format) { struct gl_priv *p = vo->priv; @@ -1460,6 +1511,14 @@ static int control(struct vo *vo, uint32_t request, void *data) do_render_osd(vo, 2); flip_page(vo); return VO_TRUE; + case VOCTRL_SCREENSHOT: { + struct voctrl_screenshot_args *args = data; + if (args->full_window) + args->out_image = get_window_screenshot(vo); + else + args->out_image = get_screenshot(vo); + return true; + } } return VO_NOTIMPL; } -- cgit v1.2.3 From 3215ec05fe27b1e07c863aadf4402c5bd6dbec33 Mon Sep 17 00:00:00 2001 From: wm4 Date: Thu, 6 Oct 2011 20:46:01 +0200 Subject: vo_xv: implement screenshots Caveat: the OSD will be included in the screenshots when the screenshot is taken during normal playback. This doesn't happen when a screenshot is taken while playback is paused. Fixing this would introduce a small performance reduction during normal playback, which is unacceptable for a possibly rarely used optional feature. Due to the nature of the Xv API, taking a screenshot of the scaled video isn't possible either. --- libvo/vo_xv.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/libvo/vo_xv.c b/libvo/vo_xv.c index 3c8b040195..694dbc048a 100644 --- a/libvo/vo_xv.c +++ b/libvo/vo_xv.c @@ -98,6 +98,8 @@ struct xvctx { uint32_t image_width; uint32_t image_height; uint32_t image_format; + uint32_t image_d_width; + uint32_t image_d_height; int is_paused; struct vo_rect src_rect; struct vo_rect dst_rect; @@ -212,6 +214,8 @@ static int config(struct vo *vo, uint32_t width, uint32_t height, ctx->image_height = height; ctx->image_width = width; ctx->image_format = format; + ctx->image_d_width = d_width; + ctx->image_d_height = d_height; if ((ctx->max_width != 0 && ctx->max_height != 0) && (ctx->image_width > ctx->max_width @@ -521,6 +525,48 @@ static int draw_slice(struct vo *vo, uint8_t *image[], int stride[], int w, return 0; } +static mp_image_t *get_screenshot(struct vo *vo) { + struct xvctx *ctx = vo->priv; + + // try to get an image without OSD + if (ctx->have_visible_image_copy) + copy_backup_image(vo, ctx->visible_buf, ctx->num_buffers); + + XvImage *xv_image = ctx->xvimage[ctx->visible_buf]; + + int w = xv_image->width; + int h = xv_image->height; + + mp_image_t *image = alloc_mpi(w, h, ctx->image_format); + + int bytes = 1; + if (!(image->flags & MP_IMGFLAG_PLANAR) && (image->flags & MP_IMGFLAG_YUV)) + // packed YUV + bytes = image->bpp / 8; + + memcpy_pic(image->planes[0], xv_image->data + xv_image->offsets[0], + bytes * w, h, image->stride[0], xv_image->pitches[0]); + + if (image->flags & MP_IMGFLAG_PLANAR) { + int swap = ctx->image_format == IMGFMT_YV12; + int p1 = swap ? 2 : 1; + int p2 = swap ? 1 : 2; + + w /= 2; + h /= 2; + + memcpy_pic(image->planes[p1], xv_image->data + xv_image->offsets[1], + w, h, image->stride[p1], xv_image->pitches[1]); + memcpy_pic(image->planes[p2], xv_image->data + xv_image->offsets[2], + w, h, image->stride[p2], xv_image->pitches[2]); + } + + image->w = ctx->image_d_width; + image->h = ctx->image_d_height; + + return image; +} + static uint32_t draw_image(struct vo *vo, mp_image_t *mpi) { struct xvctx *ctx = vo->priv; @@ -829,6 +875,11 @@ static int control(struct vo *vo, uint32_t request, void *data) return VO_TRUE; case VOCTRL_REDRAW_OSD: return redraw_osd(vo, data); + case VOCTRL_SCREENSHOT: { + struct voctrl_screenshot_args *args = data; + args->out_image = get_screenshot(vo); + return true; + } } return VO_NOTIMPL; } -- cgit v1.2.3