summaryrefslogtreecommitdiffstats
path: root/video
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2013-08-18 05:12:21 +0200
committerwm4 <wm4@nowhere>2013-08-18 05:46:02 +0200
commit4b506525da87950b68620c5788453aa89bf4def2 (patch)
tree52ad3a45678d26fa6a17d35624e2dfab6d98e037 /video
parent31ce3fcf9d7ece35489fee7c3783312045170576 (diff)
downloadmpv-4b506525da87950b68620c5788453aa89bf4def2.tar.bz2
mpv-4b506525da87950b68620c5788453aa89bf4def2.tar.xz
vo_vdpau: add RGB support
Apparently this was dropped some years ago, but judging from MPlayer's handling of this, the original code wasn't so great anyway. The new code handling clearing of panscan borders correctly, and integrates better with the YUV path. (Although the VDPAU API sure makes this annoying with its separate surface types for RGB.) Note that we create 5 surfaces for some reason - I don't think this makes too much sense (because we can't use the deinterlacer with RGB surfaces), but at least it reduces the amount of differences with the YUV code path. Clearing the borders is done by drawing a single black pixel over the window. This sounds pretty dumb, but it appears to work well, and there is no other API for that. (One could try to use the video mixer for this purpose, since it has all kinds of features, including compositing multiple RGBA surfaces and clearing the window background. But it would require an invisible dummy video surface to make the video mixer happy, and that's getting too messy.)
Diffstat (limited to 'video')
-rw-r--r--video/out/vo_vdpau.c168
1 files changed, 154 insertions, 14 deletions
diff --git a/video/out/vo_vdpau.c b/video/out/vo_vdpau.c
index 973ca235da..2b4198ebfb 100644
--- a/video/out/vo_vdpau.c
+++ b/video/out/vo_vdpau.c
@@ -87,16 +87,22 @@ struct vdpctx {
VdpOutputSurface output_surfaces[MAX_OUTPUT_SURFACES];
VdpOutputSurface screenshot_surface;
int num_output_surfaces;
+ VdpOutputSurface rgb_surfaces[NUM_BUFFERED_VIDEO];
+ VdpOutputSurface black_pixel;
struct buffered_video_surface {
+ // Either surface or rgb_surface is used (never both)
VdpVideoSurface surface;
+ VdpOutputSurface rgb_surface;
double pts;
mp_image_t *mpi;
} buffered_video[NUM_BUFFERED_VIDEO];
int deint_queue_pos;
int output_surface_width, output_surface_height;
+ int force_yuv;
VdpVideoMixer video_mixer;
struct mp_csp_details colorspace;
+ int user_deint;
int deint;
int deint_type;
int pullup;
@@ -136,6 +142,7 @@ struct vdpctx {
uint32_t image_format;
VdpChromaType vdp_chroma_type;
VdpYCbCrFormat vdp_pixel_format;
+ bool rgb_mode;
// OSD
struct osd_bitmap_surface {
@@ -218,8 +225,32 @@ static int render_video_to_output_surface(struct vo *vo,
return -1;
struct buffered_video_surface *bv = vc->buffered_video;
- int field = VDP_VIDEO_MIXER_PICTURE_STRUCTURE_FRAME;
unsigned int dp = vc->deint_queue_pos;
+
+ vdp_st = vdp->presentation_queue_block_until_surface_idle(vc->flip_queue,
+ output_surface,
+ &dummy);
+ CHECK_ST_WARNING("Error when calling "
+ "vdp_presentation_queue_block_until_surface_idle");
+
+ if (vc->rgb_mode) {
+ int flags = VDP_OUTPUT_SURFACE_RENDER_ROTATE_0;
+ vdp_st = vdp->output_surface_render_output_surface(output_surface,
+ NULL, vc->black_pixel,
+ NULL, NULL, NULL,
+ flags);
+ CHECK_ST_WARNING("Error clearing screen");
+ vdp_st = vdp->output_surface_render_output_surface(output_surface,
+ output_rect,
+ bv[dp/2].rgb_surface,
+ video_rect,
+ NULL, NULL, flags);
+ CHECK_ST_WARNING("Error when calling "
+ "vdp_output_surface_render_output_surface");
+ return 0;
+ }
+
+ int field = VDP_VIDEO_MIXER_PICTURE_STRUCTURE_FRAME;
// dp==0 means last field of latest frame, 1 earlier field of latest frame,
// 2 last field of previous frame and so on
if (vc->deint) {
@@ -231,11 +262,6 @@ static int render_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};
- vdp_st = vdp->presentation_queue_block_until_surface_idle(vc->flip_queue,
- output_surface,
- &dummy);
- CHECK_ST_WARNING("Error when calling "
- "vdp_presentation_queue_block_until_surface_idle");
vdp_st = vdp->video_mixer_render(vc->video_mixer, VDP_INVALID_HANDLE,
0, field, 2, past_fields,
@@ -306,6 +332,7 @@ static void set_next_frame_info(struct vo *vo, bool eof)
}
static void add_new_video_surface(struct vo *vo, VdpVideoSurface surface,
+ VdpOutputSurface rgb_surface,
struct mp_image *reserved_mpi, double pts)
{
struct vdpctx *vc = vo->priv;
@@ -318,6 +345,7 @@ static void add_new_video_surface(struct vo *vo, VdpVideoSurface surface,
bv[0] = (struct buffered_video_surface){
.mpi = reserved_mpi,
.surface = surface,
+ .rgb_surface = rgb_surface,
.pts = pts,
};
@@ -337,6 +365,7 @@ static void forget_frames(struct vo *vo)
mp_image_unrefp(&p->mpi);
*p = (struct buffered_video_surface){
.surface = VDP_INVALID_HANDLE,
+ .rgb_surface = VDP_INVALID_HANDLE,
};
}
}
@@ -549,6 +578,9 @@ static int set_video_attribute(struct vdpctx *vc, VdpVideoMixerAttribute attr,
struct vdp_functions *vdp = vc->vdp;
VdpStatus vdp_st;
+ if (vc->rgb_mode)
+ return -1;
+
vdp_st = vdp->video_mixer_set_attribute_values(vc->video_mixer, 1, &attr,
&value);
if (vdp_st != VDP_STATUS_OK) {
@@ -680,11 +712,35 @@ static void free_video_specific(struct vo *vo)
CHECK_ST_WARNING("Error when calling vdp_output_surface_destroy");
}
vc->screenshot_surface = VDP_INVALID_HANDLE;
+
+ for (int n = 0; n < NUM_BUFFERED_VIDEO; n++) {
+ if (vc->rgb_surfaces[n] != VDP_INVALID_HANDLE) {
+ vdp_st = vdp->output_surface_destroy(vc->rgb_surfaces[n]);
+ CHECK_ST_WARNING("Error when calling vdp_output_surface_destroy");
+ }
+ vc->rgb_surfaces[n] = VDP_INVALID_HANDLE;
+ }
+
+ if (vc->black_pixel != VDP_INVALID_HANDLE) {
+ vdp_st = vdp->output_surface_destroy(vc->black_pixel);
+ CHECK_ST_WARNING("Error when calling vdp_output_surface_destroy");
+ }
+ vc->black_pixel = VDP_INVALID_HANDLE;
+}
+
+static int get_rgb_format(int imgfmt)
+{
+ switch (imgfmt) {
+ case IMGFMT_BGR32: return VDP_RGBA_FORMAT_B8G8R8A8;
+ default: return -1;
+ }
}
static int initialize_vdpau_objects(struct vo *vo)
{
struct vdpctx *vc = vo->priv;
+ struct vdp_functions *vdp = vc->vdp;
+ VdpStatus vdp_st;
mp_vdpau_get_format(vc->image_format, &vc->vdp_chroma_type,
&vc->vdp_pixel_format);
@@ -692,8 +748,27 @@ static int initialize_vdpau_objects(struct vo *vo)
if (win_x11_init_vdpau_flip_queue(vo) < 0)
return -1;
- if (create_vdp_mixer(vo, vc->vdp_chroma_type) < 0)
- return -1;
+ if (vc->rgb_mode) {
+ int format = get_rgb_format(vc->image_format);
+ for (int n = 0; n < NUM_BUFFERED_VIDEO; n++) {
+ vdp_st = vdp->output_surface_create(vc->vdp_device,
+ format,
+ vc->vid_width, vc->vid_height,
+ &vc->rgb_surfaces[n]);
+ CHECK_ST_ERROR("Allocating RGB surface");
+ }
+ vdp_st = vdp->output_surface_create(vc->vdp_device, OUTPUT_RGBA_FORMAT,
+ 1, 1, &vc->black_pixel);
+ CHECK_ST_ERROR("Allocating clearing surface");
+ const char data[4] = {0};
+ vdp_st = vdp->output_surface_put_bits_native(vc->black_pixel,
+ (const void*[]){data},
+ (uint32_t[]){4}, NULL);
+ CHECK_ST_ERROR("Initializing clearing surface");
+ } else {
+ if (create_vdp_mixer(vo, vc->vdp_chroma_type) < 0)
+ return -1;
+ }
forget_frames(vo);
resize(vo);
@@ -706,7 +781,10 @@ static void mark_vdpau_objects_uninitialized(struct vo *vo)
for (int i = 0; i < MAX_VIDEO_SURFACES; i++)
vc->video_surfaces[i].surface = VDP_INVALID_HANDLE;
+ for (int i = 0; i < NUM_BUFFERED_VIDEO; i++)
+ vc->rgb_surfaces[i] = VDP_INVALID_HANDLE;
forget_frames(vo);
+ vc->black_pixel = VDP_INVALID_HANDLE;
vc->video_mixer = VDP_INVALID_HANDLE;
vc->flip_queue = VDP_INVALID_HANDLE;
vc->flip_target = VDP_INVALID_HANDLE;
@@ -787,6 +865,10 @@ static int config(struct vo *vo, uint32_t width, uint32_t height,
vc->vid_width = width;
vc->vid_height = height;
+ vc->rgb_mode = get_rgb_format(format) >= 0;
+
+ vc->deint = vc->rgb_mode ? 0 : vc->user_deint;
+
free_video_specific(vo);
vo_x11_config_vo_window(vo, NULL, vo->dx, vo->dy, d_width, d_height,
@@ -1200,16 +1282,53 @@ static struct mp_image *get_video_surface(struct mp_vdpau_ctx *ctx, int fmt,
return NULL;
}
+static VdpOutputSurface get_rgb_surface(struct vo *vo)
+{
+ struct vdpctx *vc = vo->priv;
+
+ assert(vc->rgb_mode);
+
+ for (int n = 0; n < NUM_BUFFERED_VIDEO; n++) {
+ VdpOutputSurface surface = vc->rgb_surfaces[n];
+ // Note: we expect to be called before add_new_video_surface(), which
+ // will lead to vc->buffered_video[NUM_BUFFERED_VIDEO - 1] to be
+ // marked unused. So this entries rgb_surface can be reused
+ // freely.
+ for (int i = 0; i < NUM_BUFFERED_VIDEO - 1; i++) {
+ if (vc->buffered_video[i].rgb_surface == surface)
+ goto in_use;
+ }
+ return surface;
+ in_use:;
+ }
+
+ mp_msg(MSGT_VO, MSGL_ERR, "[vdpau] no surfaces available in "
+ "get_rgb_surface\n");
+ return VDP_INVALID_HANDLE;
+}
+
static void draw_image(struct vo *vo, mp_image_t *mpi)
{
struct vdpctx *vc = vo->priv;
struct vdp_functions *vdp = vc->vdp;
struct mp_image *reserved_mpi = NULL;
VdpVideoSurface surface = VDP_INVALID_HANDLE;
+ VdpOutputSurface rgb_surface = VDP_INVALID_HANDLE;
+ VdpStatus vdp_st;
if (IMGFMT_IS_VDPAU(vc->image_format)) {
surface = (VdpVideoSurface)(intptr_t)mpi->planes[3];
reserved_mpi = mp_image_new_ref(mpi);
+ } else if (vc->rgb_mode) {
+ rgb_surface = get_rgb_surface(vo);
+ if (rgb_surface != VDP_INVALID_HANDLE) {
+ vdp_st = vdp->output_surface_put_bits_native(rgb_surface,
+ &(const void *){mpi->planes[0]},
+ &(uint32_t){mpi->stride[0]},
+ NULL);
+ CHECK_ST_WARNING("Error when calling "
+ "output_surface_put_bits_native");
+ }
} else {
reserved_mpi = get_video_surface(&vc->mpvdp, IMGFMT_VDPAU,
vc->vdp_chroma_type, mpi->w, mpi->h);
@@ -1233,7 +1352,7 @@ static void draw_image(struct vo *vo, mp_image_t *mpi)
else
vc->top_field_first = 1;
- add_new_video_surface(vo, surface, reserved_mpi, mpi->pts);
+ add_new_video_surface(vo, surface, rgb_surface, reserved_mpi, mpi->pts);
return;
}
@@ -1296,12 +1415,17 @@ static struct mp_image *get_window_screenshot(struct vo *vo)
return image;
}
-
static int query_format(struct vo *vo, uint32_t format)
{
- if (!mp_vdpau_get_format(format, NULL, NULL))
- return 0;
- return VFCAP_CSP_SUPPORTED | VFCAP_CSP_SUPPORTED_BY_HW | VFCAP_FLIP;
+ struct vdpctx *vc = vo->priv;
+
+ int flags = VFCAP_CSP_SUPPORTED | VFCAP_CSP_SUPPORTED_BY_HW | VFCAP_FLIP;
+ if (mp_vdpau_get_format(format, NULL, NULL))
+ return flags;
+ int rgb_format = get_rgb_format(format);
+ if (!vc->force_yuv && rgb_format >= 0)
+ return flags;
+ return 0;
}
static void destroy_vdpau_objects(struct vo *vo)
@@ -1399,6 +1523,10 @@ static int preinit(struct vo *vo)
static int get_equalizer(struct vo *vo, const char *name, int *value)
{
struct vdpctx *vc = vo->priv;
+
+ if (vc->rgb_mode)
+ return false;
+
return mp_csp_equalizer_get(&vc->video_eq, name, value) >= 0 ?
VO_TRUE : VO_NOTIMPL;
}
@@ -1407,6 +1535,9 @@ static int set_equalizer(struct vo *vo, const char *name, int value)
{
struct vdpctx *vc = vo->priv;
+ if (vc->rgb_mode)
+ return false;
+
if (mp_csp_equalizer_set(&vc->video_eq, name, value) < 0)
return VO_NOTIMPL;
@@ -1431,10 +1562,14 @@ static int control(struct vo *vo, uint32_t request, void *data)
switch (request) {
case VOCTRL_GET_DEINTERLACE:
+ if (vc->rgb_mode)
+ break;
*(int *)data = vc->deint;
return VO_TRUE;
case VOCTRL_SET_DEINTERLACE:
- vc->deint = *(int *)data;
+ if (vc->rgb_mode)
+ break;
+ vc->deint = vc->user_deint = *(int *)data;
if (vc->deint)
vc->deint = vc->deint_type;
if (vc->deint_type > 2 && status_ok(vo)) {
@@ -1475,12 +1610,16 @@ static int control(struct vo *vo, uint32_t request, void *data)
return get_equalizer(vo, args->name, args->valueptr);
}
case VOCTRL_SET_YUV_COLORSPACE:
+ if (vc->rgb_mode)
+ break;
vc->colorspace = *(struct mp_csp_details *)data;
if (status_ok(vo))
update_csc_matrix(vo);
vo->want_redraw = true;
return true;
case VOCTRL_GET_YUV_COLORSPACE:
+ if (vc->rgb_mode)
+ break;
*(struct mp_csp_details *)data = vc->colorspace;
return true;
case VOCTRL_NEWFRAME:
@@ -1559,6 +1698,7 @@ const struct vo_driver video_out_vdpau = {
.defval = &(const struct m_color) {
.r = 2, .g = 5, .b = 7, .a = 255,
}),
+ OPT_FLAG("force-yuv", force_yuv, 0),
{NULL},
}
};