summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorwm4 <wm4@mplayer2.org>2011-11-27 19:58:04 +0100
committerwm4 <wm4@mplayer2.org>2012-03-17 21:06:30 +0100
commit7c6a4e1e98c29ea853c6f7fdf9a3e5a3668006b5 (patch)
treed41d98a0c0c4492be4466a72f205145ab58c1f52
parentd813a4ede8b9fcd5e06d808be794061d7183b36d (diff)
downloadmpv-7c6a4e1e98c29ea853c6f7fdf9a3e5a3668006b5.tar.bz2
mpv-7c6a4e1e98c29ea853c6f7fdf9a3e5a3668006b5.tar.xz
vo_direct3d: implement screenshots
There are 4 code paths when taking a screenshot: - textured rendering mode - StretchRect rendering mode with planar formats - StretchRect with packed formats - full-window screenshot mode The implementation of the full-window mode (capturing the window contents, instead of the video) is very inefficient: it will create a surface of desktop size, copy the desktop contents, allocate a new memory image, and copy in the window contents. The code in screenshot.c will (as of now) allocate and convert the image from BGR to RGB, and allocate a destination buffer for the libavcodec PNG writer. If parts of the mplayer window are obscured, the full-window mode wil contain these parts as seen on the screen. Parts of the window that are outside the bounds of the desktop are clipped away. It is not known whether full-window mode works on multi-monitor setups.
-rw-r--r--libvo/vo_direct3d.c186
1 files changed, 186 insertions, 0 deletions
diff --git a/libvo/vo_direct3d.c b/libvo/vo_direct3d.c
index c7d115d5f4..737bad593e 100644
--- a/libvo/vo_direct3d.c
+++ b/libvo/vo_direct3d.c
@@ -147,6 +147,8 @@ typedef struct d3d_priv {
fullscreen */
int src_width; /**< Source (movie) width */
int src_height; /**< Source (movie) heigth */
+ int src_d_width; /**< Source (movie) aspect corrected width */
+ int src_d_height; /**< Source (movie) aspect corrected heigth */
int border_x; /**< horizontal border value for OSD */
int border_y; /**< vertical border value for OSD */
int image_format; /**< mplayer image format */
@@ -245,6 +247,8 @@ static int draw_slice(struct vo *vo, uint8_t *src[], int stride[], int w, int h,
static void uninit(struct vo *vo);
static void draw_osd(struct vo *vo, struct osd_state *osd);
static void flip_page(struct vo *vo);
+static mp_image_t *get_screenshot(d3d_priv *priv);
+static mp_image_t *get_window_screenshot(d3d_priv *priv);
static void d3d_matrix_identity(D3DMATRIX *m)
@@ -1568,6 +1572,14 @@ static int control(struct vo *vo, uint32_t request, void *data)
r->mt = r->mb = priv->border_y;
return VO_TRUE;
}
+ case VOCTRL_SCREENSHOT: {
+ struct voctrl_screenshot_args *args = data;
+ if (args->full_window)
+ args->out_image = get_window_screenshot(priv);
+ else
+ args->out_image = get_screenshot(priv);
+ return !!args->out_image;
+ }
}
return VO_NOTIMPL;
}
@@ -1596,6 +1608,9 @@ static int config(struct vo *vo, uint32_t width, uint32_t height,
return VO_ERROR;
}
+ priv->src_d_width = d_width;
+ priv->src_d_height = d_height;
+
if ((priv->image_format != format)
|| (priv->src_width != width)
|| (priv->src_height != height))
@@ -1763,6 +1778,177 @@ static int draw_slice(struct vo *vo, uint8_t *src[], int stride[], int w, int h,
return 0; /* Success */
}
+static bool get_screenshot_from_surface(d3d_priv *priv, mp_image_t *image)
+{
+ if (!priv->locked_rect.pBits) {
+ if (FAILED(IDirect3DSurface9_LockRect(priv->d3d_surface,
+ &priv->locked_rect, NULL, 0))) {
+ mp_msg(MSGT_VO, MSGL_ERR, "<vo_direct3d>Surface lock failed.\n");
+ return false;
+ }
+ }
+
+ if (image->flags & MP_IMGFLAG_PLANAR) {
+ char *src;
+ int w = priv->src_width;
+ int h = priv->src_height;
+ int swapped = priv->movie_src_fmt == MAKEFOURCC('Y','V','1','2');
+ int plane1 = swapped ? 2 : 1;
+ int plane2 = swapped ? 1 : 2;
+
+ int uv_stride = priv->locked_rect.Pitch / 2;
+
+ /* Copy Y */
+ src = priv->locked_rect.pBits;
+ memcpy_pic(image->planes[0], src, w, h, priv->locked_rect.Pitch,
+ image->stride[0]);
+
+ w /= 2;
+ h /= 2;
+
+ /* Copy U */
+ src = priv->locked_rect.pBits;
+ src = src + priv->locked_rect.Pitch * priv->src_height;
+
+ memcpy_pic(image->planes[plane1], src, w, h, uv_stride,
+ image->stride[1]);
+
+ /* Copy V */
+ src = priv->locked_rect.pBits;
+ src = src + priv->locked_rect.Pitch * priv->src_height
+ + uv_stride * (priv->src_height / 2);
+
+ memcpy_pic(image->planes[plane2], src, w, h, uv_stride,
+ image->stride[2]);
+ } else {
+ // packed YUV or RGB
+ memcpy_pic(image->planes[0], priv->locked_rect.pBits, image->stride[0],
+ image->height, priv->locked_rect.Pitch, image->stride[0]);
+ }
+
+ d3d_unlock_video_objects(priv);
+ return true;
+}
+
+static bool get_screenshot_from_texture(d3d_priv *priv, mp_image_t *image)
+{
+ if (!d3d_lock_video_textures(priv)) {
+ d3d_unlock_video_objects(priv);
+ return false;
+ }
+
+ assert(image->num_planes == priv->plane_count);
+
+ for (int n = 0; n < priv->plane_count; n++) {
+ struct texplane *plane = &priv->planes[n];
+
+ int width = priv->src_width >> plane->shift_x;
+ int height = priv->src_height >> plane->shift_x;
+
+ memcpy_pic(image->planes[n], plane->locked_rect.pBits,
+ width * plane->bytes_per_pixel, height,
+ image->stride[n], plane->locked_rect.Pitch);
+ }
+
+ return true;
+}
+
+static mp_image_t *get_screenshot(d3d_priv *priv)
+{
+ if (!priv->d3d_device)
+ return NULL;
+
+ mp_image_t *image = alloc_mpi(priv->src_width, priv->src_height,
+ priv->image_format);
+
+ bool res;
+
+ if (priv->use_textures)
+ res = get_screenshot_from_texture(priv, image);
+ else
+ res = get_screenshot_from_surface(priv, image);
+
+ if (!res) {
+ free_mp_image(image);
+ return NULL;
+ }
+
+ image->w = priv->src_d_width;
+ image->h = priv->src_d_height;
+
+ return image;
+}
+
+static mp_image_t *get_window_screenshot(d3d_priv *priv)
+{
+ D3DDISPLAYMODE mode;
+ mp_image_t *image = NULL;
+ RECT window_rc;
+ RECT screen_rc;
+ RECT visible;
+ POINT pt;
+ D3DLOCKED_RECT locked_rect;
+ int width, height;
+
+ if (FAILED(IDirect3DDevice9_GetDisplayMode(priv->d3d_device, 0, &mode))) {
+ mp_msg(MSGT_VO,MSGL_ERR, "<vo_direct3d>GetDisplayMode failed.\n");
+ goto error_exit;
+ }
+
+ IDirect3DSurface9 *surface = NULL;
+ if (FAILED(IDirect3DDevice9_CreateOffscreenPlainSurface(priv->d3d_device,
+ mode.Width, mode.Height, D3DFMT_A8R8G8B8, D3DPOOL_SYSTEMMEM, &surface,
+ NULL)))
+ {
+ mp_msg(MSGT_VO,MSGL_ERR, "<vo_direct3d>Couldn't create surface.\n");
+ goto error_exit;
+ }
+
+ if (FAILED(IDirect3DDevice9_GetFrontBufferData(priv->d3d_device, 0,
+ surface)))
+ {
+ mp_msg(MSGT_VO,MSGL_ERR, "<vo_direct3d>Couldn't copy frontbuffer.\n");
+ goto error_exit;
+ }
+
+ GetClientRect(vo_w32_window, &window_rc);
+ pt = (POINT) { 0, 0 };
+ ClientToScreen(vo_w32_window, &pt);
+ window_rc.left = pt.x;
+ window_rc.top = pt.y;
+ window_rc.right += window_rc.left;
+ window_rc.bottom += window_rc.top;
+
+ screen_rc = (RECT) { 0, 0, mode.Width, mode.Height };
+
+ if (!IntersectRect(&visible, &screen_rc, &window_rc))
+ goto error_exit;
+ width = visible.right - visible.left;
+ height = visible.bottom - visible.top;
+ if (width < 1 || height < 1)
+ goto error_exit;
+
+ image = alloc_mpi(width, height, IMGFMT_BGR32);
+
+ IDirect3DSurface9_LockRect(surface, &locked_rect, NULL, 0);
+
+ memcpy_pic(image->planes[0], (char*)locked_rect.pBits + visible.top *
+ locked_rect.Pitch + visible.left * 4, width * 4, height,
+ image->stride[0], locked_rect.Pitch);
+
+ IDirect3DSurface9_UnlockRect(surface);
+ IDirect3DSurface9_Release(surface);
+
+ return image;
+
+error_exit:
+ if (image)
+ free_mp_image(image);
+ if (surface)
+ IDirect3DSurface9_Release(surface);
+ return NULL;
+}
+
/** @brief Maps MPlayer alpha to D3D
* 0x0 -> transparent and discarded by alpha test
* 0x1 -> 0xFF to become opaque