summaryrefslogtreecommitdiffstats
path: root/libvo
diff options
context:
space:
mode:
Diffstat (limited to 'libvo')
-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
7 files changed, 240 insertions, 11 deletions
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;
+}
+
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;
}
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;
}