summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorUoti Urpala <uau@mplayer2.org>2011-11-25 23:59:23 +0200
committerUoti Urpala <uau@mplayer2.org>2011-11-25 23:59:23 +0200
commit8d6fc26bb982c5ecb7dbe4c20d51dec47f82abbf (patch)
tree60e7fca2028093bf454f0d62bd96b40c3e4ab7a4
parent9fae75b81ce2eb475db7652083b388e704833a72 (diff)
parent3215ec05fe27b1e07c863aadf4402c5bd6dbec33 (diff)
downloadmpv-8d6fc26bb982c5ecb7dbe4c20d51dec47f82abbf.tar.bz2
mpv-8d6fc26bb982c5ecb7dbe4c20d51dec47f82abbf.tar.xz
Merge branch 'screenshot' (early part)
-rw-r--r--DOCS/tech/slave.txt11
-rw-r--r--Makefile1
-rw-r--r--TOOLS/vdpau_functions.py1
-rw-r--r--command.c11
-rw-r--r--input/input.c4
-rw-r--r--libmpcodecs/mp_image.c34
-rw-r--r--libmpcodecs/vf.h8
-rw-r--r--libmpcodecs/vf_screenshot.c181
-rw-r--r--libvo/gl_common.c19
-rw-r--r--libvo/gl_common.h2
-rw-r--r--libvo/vdpau_template.c1
-rw-r--r--libvo/video_out.h19
-rw-r--r--libvo/vo_gl.c59
-rw-r--r--libvo/vo_vdpau.c100
-rw-r--r--libvo/vo_xv.c51
-rw-r--r--mp_core.h2
-rw-r--r--mplayer.c7
-rw-r--r--screenshot.c233
-rw-r--r--screenshot.h41
19 files changed, 613 insertions, 172 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 <value>
+screenshot <each_frame> <full_window>
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 <value>
Inject <value> key code event into MPlayer.
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/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/command.c b/command.c
index d5ab987baf..45f778a96c 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, 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/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 <stdlib.h>
#include <string.h>
-#if HAVE_MALLOC_H
-#include <malloc.h>
-#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);
}
diff --git a/libmpcodecs/vf.h b/libmpcodecs/vf.h
index fa58445e1e..0dac19c9f6 100644
--- a/libmpcodecs/vf.h
+++ b/libmpcodecs/vf.h
@@ -89,6 +89,12 @@ typedef struct vf_seteq {
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 // query max postprocessing level (if any)
#define VFCTRL_SET_PP_LEVEL 5 // set postprocessing level
#define VFCTRL_SET_EQUALIZER 6 // set color options (brightness,contrast etc)
@@ -98,7 +104,7 @@ typedef struct vf_seteq {
#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/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 {
@@ -635,6 +636,24 @@ void glUploadTex(GL *gl, GLenum target, GLenum format, GLenum type,
}
/**
+ * \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
* \param text if set use the GL_ATI_text_fragment_shader API as
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/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/video_out.h b/libvo/video_out.h
index d1a2a7f65a..bd7b2e8fa5 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,23 @@ 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 {
+ // 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
+ // 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/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;
}
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;