diff options
Diffstat (limited to 'libvo/vo_vdpau.c')
-rw-r--r-- | libvo/vo_vdpau.c | 2257 |
1 files changed, 1320 insertions, 937 deletions
diff --git a/libvo/vo_vdpau.c b/libvo/vo_vdpau.c index 2ee7cccf9b..3623bfd9d7 100644 --- a/libvo/vo_vdpau.c +++ b/libvo/vo_vdpau.c @@ -2,6 +2,7 @@ * VDPAU video output driver * * Copyright (C) 2008 NVIDIA + * Copyright (C) 2009 Uoti Urpala * * This file is part of MPlayer. * @@ -20,621 +21,819 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -/** - * \defgroup VDPAU_Presentation VDPAU Presentation - * \ingroup Decoder - * +/* * Actual decoding and presentation are implemented here. * All necessary frame information is collected through * the "vdpau_render_state" structure after parsing all headers * etc. in libavcodec for different codecs. - * - * @{ */ #include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <stdbool.h> +#include <limits.h> #include "config.h" #include "mp_msg.h" +#include "options.h" +#include "talloc.h" #include "video_out.h" -#include "video_out_internal.h" #include "x11_common.h" #include "aspect.h" #include "sub.h" #include "subopt-helper.h" +#include "libmpcodecs/vfcap.h" +#include "libmpcodecs/mp_image.h" +#include "osdep/timer.h" #include "libavcodec/vdpau.h" -#include "gui/interface.h" +#include "font_load.h" #include "libavutil/common.h" #include "libavutil/mathematics.h" -#include "libass/ass_mp.h" - -static vo_info_t info = { - "VDPAU with X11", - "vdpau", - "Rajib Mahapatra <rmahapatra@nvidia.com> and others", - "" -}; - -LIBVO_EXTERN(vdpau) +#include "ass_mp.h" #define CHECK_ST_ERROR(message) \ - if (vdp_st != VDP_STATUS_OK) { \ - mp_msg(MSGT_VO, MSGL_ERR, "[vdpau] %s: %s\n", \ - message, vdp_get_error_string(vdp_st)); \ - return -1; \ - } + do { \ + if (vdp_st != VDP_STATUS_OK) { \ + mp_msg(MSGT_VO, MSGL_ERR, "[vdpau] %s: %s\n", \ + message, vdp->get_error_string(vdp_st)); \ + return -1; \ + } \ + } while (0) #define CHECK_ST_WARNING(message) \ - if (vdp_st != VDP_STATUS_OK) \ - mp_msg(MSGT_VO, MSGL_WARN, "[vdpau] %s: %s\n", \ - message, vdp_get_error_string(vdp_st)); + do { \ + if (vdp_st != VDP_STATUS_OK) \ + mp_msg(MSGT_VO, MSGL_WARN, "[ vdpau] %s: %s\n", \ + message, vdp->get_error_string(vdp_st)); \ + } while (0) /* number of video and output surfaces */ #define NUM_OUTPUT_SURFACES 3 #define MAX_VIDEO_SURFACES 50 +#define NUM_BUFFERED_VIDEO 4 /* number of palette entries */ #define PALETTE_SIZE 256 -/* Initial maximum number of EOSD surfaces */ -#define EOSD_SURFACES_INITIAL 512 +/* Initial size of EOSD surface in pixels (x*x) */ +#define EOSD_SURFACE_INITIAL_SIZE 256 /* * Global variable declaration - VDPAU specific */ -/* Declaration for all variables of win_x11_init_vdpau_procs() and - * win_x11_init_vdpau_flip_queue() functions - */ -static VdpDevice vdp_device; -static VdpGetProcAddress *vdp_get_proc_address; - -static VdpPresentationQueueTarget vdp_flip_target; -static VdpPresentationQueue vdp_flip_queue; - -static VdpDeviceDestroy *vdp_device_destroy; -static VdpVideoSurfaceCreate *vdp_video_surface_create; -static VdpVideoSurfaceDestroy *vdp_video_surface_destroy; - -static VdpGetErrorString *vdp_get_error_string; +struct vdp_functions { +#define VDP_FUNCTION(vdp_type, _, mp_name) vdp_type *mp_name; +#include "vdpau_template.c" +#undef VDP_FUNCTION +}; -/* May be used in software filtering/postprocessing options - * in MPlayer (./mplayer -vf ..) if we copy video_surface data to - * system memory. - */ -static VdpVideoSurfacePutBitsYCbCr *vdp_video_surface_put_bits_y_cb_cr; -static VdpOutputSurfacePutBitsNative *vdp_output_surface_put_bits_native; - -static VdpOutputSurfaceCreate *vdp_output_surface_create; -static VdpOutputSurfaceDestroy *vdp_output_surface_destroy; - -/* VideoMixer puts video_surface data on displayable output_surface. */ -static VdpVideoMixerCreate *vdp_video_mixer_create; -static VdpVideoMixerDestroy *vdp_video_mixer_destroy; -static VdpVideoMixerRender *vdp_video_mixer_render; -static VdpVideoMixerSetFeatureEnables *vdp_video_mixer_set_feature_enables; -static VdpVideoMixerSetAttributeValues *vdp_video_mixer_set_attribute_values; - -static VdpPresentationQueueTargetDestroy *vdp_presentation_queue_target_destroy; -static VdpPresentationQueueCreate *vdp_presentation_queue_create; -static VdpPresentationQueueDestroy *vdp_presentation_queue_destroy; -static VdpPresentationQueueDisplay *vdp_presentation_queue_display; -static VdpPresentationQueueBlockUntilSurfaceIdle *vdp_presentation_queue_block_until_surface_idle; -static VdpPresentationQueueTargetCreateX11 *vdp_presentation_queue_target_create_x11; - -static VdpOutputSurfaceRenderOutputSurface *vdp_output_surface_render_output_surface; -static VdpOutputSurfacePutBitsIndexed *vdp_output_surface_put_bits_indexed; -static VdpOutputSurfaceRenderBitmapSurface *vdp_output_surface_render_bitmap_surface; - -static VdpBitmapSurfaceCreate *vdp_bitmap_surface_create; -static VdpBitmapSurfaceDestroy *vdp_bitmap_surface_destroy; -static VdpBitmapSurfacePutBitsNative *vdp_bitmap_surface_putbits_native; - -static VdpDecoderCreate *vdp_decoder_create; -static VdpDecoderDestroy *vdp_decoder_destroy; -static VdpDecoderRender *vdp_decoder_render; - -static VdpGenerateCSCMatrix *vdp_generate_csc_matrix; -static VdpPreemptionCallbackRegister *vdp_preemption_callback_register; - -/* output_surfaces[NUM_OUTPUT_SURFACES] is misused for OSD. */ -#define osd_surface output_surfaces[NUM_OUTPUT_SURFACES] -static VdpOutputSurface output_surfaces[NUM_OUTPUT_SURFACES + 1]; -static VdpVideoSurface deint_surfaces[3]; -static mp_image_t *deint_mpi[2]; -static int output_surface_width, output_surface_height; - -static VdpVideoMixer video_mixer; -static int deint; -static int deint_type; -static int deint_counter; -static int pullup; -static float denoise; -static float sharpen; -static int colorspace; -static int chroma_deint; -static int force_mixer; -static int top_field_first; -static int flip; -static int hqscaling; - -static VdpDecoder decoder; -static int decoder_max_refs; - -static VdpRect src_rect_vid; -static VdpRect out_rect_vid; -static int border_x, border_y; - -static struct vdpau_render_state surface_render[MAX_VIDEO_SURFACES]; -static int surface_num; -static int vid_surface_num; -static uint32_t vid_width, vid_height; -static uint32_t image_format; -static VdpChromaType vdp_chroma_type; -static VdpYCbCrFormat vdp_pixel_format; - -static volatile int is_preempted; - -/* draw_osd */ -static unsigned char *index_data; -static int index_data_size; -static uint32_t palette[PALETTE_SIZE]; - -// EOSD -// Pool of surfaces -struct { - VdpBitmapSurface surface; - int w; - int h; - char in_use; -} *eosd_surfaces; - -// List of surfaces to be rendered -struct { - VdpBitmapSurface surface; - VdpRect source; - VdpRect dest; - VdpColor color; -} *eosd_targets; - -static int eosd_render_count; -static int eosd_surface_count; - -// Video equalizer -static VdpProcamp procamp; +struct vdpctx { + struct vdp_functions *vdp; + + VdpDevice vdp_device; + bool is_preempted; + bool preemption_acked; + bool preemption_user_notified; + unsigned int last_preemption_retry_fail; + VdpGetProcAddress *vdp_get_proc_address; + + VdpPresentationQueueTarget flip_target; + VdpPresentationQueue flip_queue; + uint64_t last_vdp_time; + unsigned int last_sync_update; + + /* output_surfaces[NUM_OUTPUT_SURFACES] is misused for OSD. */ +#define osd_surface vc->output_surfaces[NUM_OUTPUT_SURFACES] + VdpOutputSurface output_surfaces[NUM_OUTPUT_SURFACES + 1]; + struct buffered_video_surface { + VdpVideoSurface surface; + double pts; + mp_image_t *mpi; + } buffered_video[NUM_BUFFERED_VIDEO]; + int deint_queue_pos; + int output_surface_width, output_surface_height; + + VdpVideoMixer video_mixer; + int user_colorspace; + int colorspace; + int deint; + int deint_type; + int deint_counter; + int pullup; + float denoise; + float sharpen; + int hqscaling; + int chroma_deint; + int flip_offset_window; + int flip_offset_fs; + int top_field_first; + bool flip; + + VdpDecoder decoder; + int decoder_max_refs; + + VdpRect src_rect_vid; + VdpRect out_rect_vid; + int border_x, border_y; + + struct vdpau_render_state surface_render[MAX_VIDEO_SURFACES]; + int surface_num; + VdpTime recent_vsync_time; + float user_fps; + unsigned int vsync_interval; + uint64_t last_queue_time; + uint64_t last_ideal_time; + bool dropped_frame; + uint64_t dropped_time; + uint32_t vid_width, vid_height; + uint32_t image_format; + VdpChromaType vdp_chroma_type; + VdpYCbCrFormat vdp_pixel_format; + + /* draw_osd */ + unsigned char *index_data; + int index_data_size; + uint32_t palette[PALETTE_SIZE]; + + // EOSD + // Pool of surfaces + struct eosd_bitmap_surface { + VdpBitmapSurface surface; + int w; + int h; + uint32_t max_width; + uint32_t max_height; + } eosd_surface; + + // List of surfaces to be rendered + struct eosd_target { + VdpRect source; + VdpRect dest; + VdpColor color; + } *eosd_targets; + int eosd_targets_size; + int *eosd_scratch; + + int eosd_render_count; + + // Video equalizer + VdpProcamp procamp; + + int num_shown_frames; + bool paused; + + // These tell what's been initialized and uninit() should free/uninitialize + bool mode_switched; +}; -/* - * X11 specific - */ -static int visible_buf; -static int int_pause; +static int change_vdptime_sync(struct vdpctx *vc, unsigned int *t) +{ + struct vdp_functions *vdp = vc->vdp; + VdpStatus vdp_st; + VdpTime vdp_time; + vdp_st = vdp->presentation_queue_get_time(vc->flip_queue, &vdp_time); + CHECK_ST_ERROR("Error when calling vdp_presentation_queue_get_time"); + unsigned int t1 = *t; + unsigned int t2 = GetTimer(); + uint64_t old = vc->last_vdp_time + (t1 - vc->last_sync_update) * 1000ULL; + if (vdp_time > old) + if (vdp_time > old + (t2 - t1) * 1000ULL) + vdp_time -= (t2 - t1) * 1000ULL; + else + vdp_time = old; + mp_msg(MSGT_VO, MSGL_V, "[vdpau] adjusting VdpTime offset by %f µs\n", + (int64_t)(vdp_time - old) / 1000.); + vc->last_vdp_time = vdp_time; + vc->last_sync_update = t1; + *t = t2; + return 0; +} -static void draw_eosd(void); +static uint64_t sync_vdptime(struct vo *vo) +{ + struct vdpctx *vc = vo->priv; + + unsigned int t = GetTimer(); + if (t - vc->last_sync_update > 5000000) + change_vdptime_sync(vc, &t); + uint64_t now = (t - vc->last_sync_update) * 1000ULL + vc->last_vdp_time; + // Make sure nanosecond inaccuracies don't make things inconsistent + now = FFMAX(now, vc->recent_vsync_time); + return now; +} -static void push_deint_surface(VdpVideoSurface surface) +static uint64_t convert_to_vdptime(struct vo *vo, unsigned int t) { - deint_surfaces[2] = deint_surfaces[1]; - deint_surfaces[1] = deint_surfaces[0]; - deint_surfaces[0] = surface; + struct vdpctx *vc = vo->priv; + return (int)(t - vc->last_sync_update) * 1000LL + vc->last_vdp_time; } -static void video_to_output_surface(void) +static void flip_page_timed(struct vo *vo, unsigned int pts_us, int duration); + +static int video_to_output_surface(struct vo *vo) { + struct vdpctx *vc = vo->priv; + struct vdp_functions *vdp = vc->vdp; VdpTime dummy; VdpStatus vdp_st; - int i; - if (vid_surface_num < 0) - return; + if (vc->deint_queue_pos < 0) + return -1; - if (deint < 2 || deint_surfaces[0] == VDP_INVALID_HANDLE) - push_deint_surface(surface_render[vid_surface_num].surface); + struct buffered_video_surface *bv = vc->buffered_video; + int field = VDP_VIDEO_MIXER_PICTURE_STRUCTURE_FRAME; + unsigned int dp = vc->deint_queue_pos; + // 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) { + field = vc->top_field_first ^ (dp & 1) ? + VDP_VIDEO_MIXER_PICTURE_STRUCTURE_BOTTOM_FIELD: + VDP_VIDEO_MIXER_PICTURE_STRUCTURE_TOP_FIELD; + } + const VdpVideoSurface *past_fields = (const VdpVideoSurface []){ + 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); + 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, + bv[dp/2].surface, 1, future_fields, + &vc->src_rect_vid, output_surface, + NULL, &vc->out_rect_vid, 0, NULL); + CHECK_ST_WARNING("Error when calling vdp_video_mixer_render"); + return 0; +} - for (i = 0; i <= !!(deint > 1); i++) { - int field = VDP_VIDEO_MIXER_PICTURE_STRUCTURE_FRAME; - VdpOutputSurface output_surface; - if (i) { - draw_eosd(); - draw_osd(); - flip_page(); +static void get_buffered_frame(struct vo *vo, bool eof) +{ + struct vdpctx *vc = vo->priv; + + int dqp = vc->deint_queue_pos; + if (dqp < 0) + dqp += 1000; + else + dqp = vc->deint >= 2 ? dqp - 1 : dqp - 2 | 1; + if (dqp < (eof ? 0 : 3)) + return; + + dqp = FFMIN(dqp, 4); + vc->deint_queue_pos = dqp; + vo->frame_loaded = true; + + // Set pts values + struct buffered_video_surface *bv = vc->buffered_video; + int idx = vc->deint_queue_pos >> 1; + if (idx == 0) { // no future frame/pts available + vo->next_pts = bv[0].pts; + vo->next_pts2 = MP_NOPTS_VALUE; + } else if (!(vc->deint >= 2)) { // no field-splitting deinterlace + vo->next_pts = bv[idx].pts; + vo->next_pts2 = bv[idx - 1].pts; + } else { // deinterlace with separate fields + double intermediate_pts; + double diff = bv[idx - 1].pts - bv[idx].pts; + if (diff > 0 && diff < 0.5) + intermediate_pts = (bv[idx].pts + bv[idx - 1].pts) / 2; + else + intermediate_pts = bv[idx].pts; + if (vc->deint_queue_pos & 1) { // first field + vo->next_pts = bv[idx].pts; + vo->next_pts2 = intermediate_pts; + } else { + vo->next_pts = intermediate_pts; + vo->next_pts2 = bv[idx - 1].pts; } - if (deint) - field = (top_field_first == i) ^ (deint > 1) ? - VDP_VIDEO_MIXER_PICTURE_STRUCTURE_BOTTOM_FIELD: - VDP_VIDEO_MIXER_PICTURE_STRUCTURE_TOP_FIELD; - output_surface = output_surfaces[surface_num]; - vdp_st = vdp_presentation_queue_block_until_surface_idle(vdp_flip_queue, - output_surface, - &dummy); - CHECK_ST_WARNING("Error when calling vdp_presentation_queue_block_until_surface_idle") - - vdp_st = vdp_video_mixer_render(video_mixer, VDP_INVALID_HANDLE, 0, - field, 2, deint_surfaces + 1, - deint_surfaces[0], - 1, &surface_render[vid_surface_num].surface, - &src_rect_vid, - output_surface, - NULL, &out_rect_vid, 0, NULL); - CHECK_ST_WARNING("Error when calling vdp_video_mixer_render") - push_deint_surface(surface_render[vid_surface_num].surface); + } + + video_to_output_surface(vo); +} + +static void add_new_video_surface(struct vo *vo, VdpVideoSurface surface, + struct mp_image *reserved_mpi, double pts) +{ + struct vdpctx *vc = vo->priv; + struct buffered_video_surface *bv = vc->buffered_video; + + if (reserved_mpi) + reserved_mpi->usage_count++; + if (bv[NUM_BUFFERED_VIDEO - 1].mpi) + bv[NUM_BUFFERED_VIDEO - 1].mpi->usage_count--; + + for (int i = NUM_BUFFERED_VIDEO - 1; i > 0; i--) + bv[i] = bv[i - 1]; + bv[0] = (struct buffered_video_surface){ + .mpi = reserved_mpi, + .surface = surface, + .pts = pts, + }; + + vc->deint_queue_pos += 2; + get_buffered_frame(vo, false); +} + +static void forget_frames(struct vo *vo) +{ + struct vdpctx *vc = vo->priv; + + vc->deint_queue_pos = -1001; + vc->dropped_frame = false; + for (int i = 0; i < NUM_BUFFERED_VIDEO; i++) { + struct buffered_video_surface *p = vc->buffered_video + i; + if (p->mpi) + p->mpi->usage_count--; + *p = (struct buffered_video_surface){ + .surface = VDP_INVALID_HANDLE, + }; } } -static void resize(void) +static void resize(struct vo *vo) { + struct vdpctx *vc = vo->priv; + struct vdp_functions *vdp = vc->vdp; VdpStatus vdp_st; int i; struct vo_rect src_rect; struct vo_rect dst_rect; struct vo_rect borders; - calc_src_dst_rects(vid_width, vid_height, &src_rect, &dst_rect, &borders, NULL); - out_rect_vid.x0 = dst_rect.left; - out_rect_vid.x1 = dst_rect.right; - out_rect_vid.y0 = dst_rect.top; - out_rect_vid.y1 = dst_rect.bottom; - src_rect_vid.x0 = src_rect.left; - src_rect_vid.x1 = src_rect.right; - src_rect_vid.y0 = flip ? src_rect.bottom : src_rect.top; - src_rect_vid.y1 = flip ? src_rect.top : src_rect.bottom; - border_x = borders.left; - border_y = borders.top; + calc_src_dst_rects(vo, vc->vid_width, vc->vid_height, &src_rect, &dst_rect, + &borders, NULL); + vc->out_rect_vid.x0 = dst_rect.left; + vc->out_rect_vid.x1 = dst_rect.right; + vc->out_rect_vid.y0 = dst_rect.top; + vc->out_rect_vid.y1 = dst_rect.bottom; + vc->src_rect_vid.x0 = src_rect.left; + vc->src_rect_vid.x1 = src_rect.right; + vc->src_rect_vid.y0 = vc->flip ? src_rect.bottom : src_rect.top; + vc->src_rect_vid.y1 = vc->flip ? src_rect.top : src_rect.bottom; + vc->border_x = borders.left; + vc->border_y = borders.top; #ifdef CONFIG_FREETYPE // adjust font size to display size force_load_font = 1; #endif vo_osd_changed(OSDTYPE_OSD); - - if (output_surface_width < vo_dwidth || output_surface_height < vo_dheight) { - if (output_surface_width < vo_dwidth) { - output_surface_width += output_surface_width >> 1; - output_surface_width = FFMAX(output_surface_width, vo_dwidth); + int flip_offset_ms = vo_fs ? vc->flip_offset_fs : vc->flip_offset_window; + vo->flip_queue_offset = flip_offset_ms / 1000.; + + 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) { + vc->output_surface_width += vc->output_surface_width >> 1; + vc->output_surface_width = FFMAX(vc->output_surface_width, + vo->dwidth); } - if (output_surface_height < vo_dheight) { - output_surface_height += output_surface_height >> 1; - output_surface_height = FFMAX(output_surface_height, vo_dheight); + if (vc->output_surface_height < vo->dheight) { + vc->output_surface_height += vc->output_surface_height >> 1; + vc->output_surface_height = FFMAX(vc->output_surface_height, + vo->dheight); } // Creation of output_surfaces for (i = 0; i <= NUM_OUTPUT_SURFACES; i++) { - if (output_surfaces[i] != VDP_INVALID_HANDLE) - vdp_output_surface_destroy(output_surfaces[i]); - vdp_st = vdp_output_surface_create(vdp_device, VDP_RGBA_FORMAT_B8G8R8A8, - output_surface_width, output_surface_height, - &output_surfaces[i]); - CHECK_ST_WARNING("Error when calling vdp_output_surface_create") - mp_msg(MSGT_VO, MSGL_DBG2, "OUT CREATE: %u\n", output_surfaces[i]); + if (vc->output_surfaces[i] != VDP_INVALID_HANDLE) + vdp->output_surface_destroy(vc->output_surfaces[i]); + vdp_st = vdp->output_surface_create(vc->vdp_device, + VDP_RGBA_FORMAT_B8G8R8A8, + vc->output_surface_width, + vc->output_surface_height, + &vc->output_surfaces[i]); + CHECK_ST_WARNING("Error when calling vdp_output_surface_create"); + mp_msg(MSGT_VO, MSGL_DBG2, "OUT CREATE: %u\n", + vc->output_surfaces[i]); } + vc->num_shown_frames = 0; } - if (image_format == IMGFMT_BGRA) { - vdp_st = vdp_output_surface_render_output_surface(output_surfaces[surface_num], - NULL, VDP_INVALID_HANDLE, - NULL, NULL, NULL, - VDP_OUTPUT_SURFACE_RENDER_ROTATE_0); - CHECK_ST_WARNING("Error when calling vdp_output_surface_render_output_surface") - vdp_st = vdp_output_surface_render_output_surface(output_surfaces[1 - surface_num], - NULL, VDP_INVALID_HANDLE, - NULL, NULL, NULL, - VDP_OUTPUT_SURFACE_RENDER_ROTATE_0); - CHECK_ST_WARNING("Error when calling vdp_output_surface_render_output_surface") - } else - video_to_output_surface(); - if (visible_buf) - flip_page(); + if (vc->paused && had_frames) + if (video_to_output_surface(vo) >= 0) + flip_page_timed(vo, 0, -1); } static void preemption_callback(VdpDevice device, void *context) { - mp_msg(MSGT_VO, MSGL_ERR, "[vdpau] Display preemption detected\n"); - is_preempted = 1; + struct vdpctx *vc = context; + vc->is_preempted = true; + vc->preemption_acked = false; } /* Initialize vdp_get_proc_address, called from preinit() */ -static int win_x11_init_vdpau_procs(void) +static int win_x11_init_vdpau_procs(struct vo *vo) { + struct vo_x11_state *x11 = vo->x11; + struct vdpctx *vc = vo->priv; + talloc_free(vc->vdp); // In case this is reinitialization after preemption + struct vdp_functions *vdp = talloc_zero(vc, struct vdp_functions); + vc->vdp = vdp; VdpStatus vdp_st; struct vdp_function { const int id; - void *pointer; + int offset; }; const struct vdp_function *dsc; static const struct vdp_function vdp_func[] = { - {VDP_FUNC_ID_GET_ERROR_STRING, &vdp_get_error_string}, - {VDP_FUNC_ID_DEVICE_DESTROY, &vdp_device_destroy}, - {VDP_FUNC_ID_VIDEO_SURFACE_CREATE, &vdp_video_surface_create}, - {VDP_FUNC_ID_VIDEO_SURFACE_DESTROY, &vdp_video_surface_destroy}, - {VDP_FUNC_ID_VIDEO_SURFACE_PUT_BITS_Y_CB_CR, - &vdp_video_surface_put_bits_y_cb_cr}, - {VDP_FUNC_ID_OUTPUT_SURFACE_PUT_BITS_NATIVE, - &vdp_output_surface_put_bits_native}, - {VDP_FUNC_ID_OUTPUT_SURFACE_CREATE, &vdp_output_surface_create}, - {VDP_FUNC_ID_OUTPUT_SURFACE_DESTROY, &vdp_output_surface_destroy}, - {VDP_FUNC_ID_VIDEO_MIXER_CREATE, &vdp_video_mixer_create}, - {VDP_FUNC_ID_VIDEO_MIXER_DESTROY, &vdp_video_mixer_destroy}, - {VDP_FUNC_ID_VIDEO_MIXER_RENDER, &vdp_video_mixer_render}, - {VDP_FUNC_ID_VIDEO_MIXER_SET_FEATURE_ENABLES, - &vdp_video_mixer_set_feature_enables}, - {VDP_FUNC_ID_VIDEO_MIXER_SET_ATTRIBUTE_VALUES, - &vdp_video_mixer_set_attribute_values}, - {VDP_FUNC_ID_PRESENTATION_QUEUE_TARGET_DESTROY, - &vdp_presentation_queue_target_destroy}, - {VDP_FUNC_ID_PRESENTATION_QUEUE_CREATE, &vdp_presentation_queue_create}, - {VDP_FUNC_ID_PRESENTATION_QUEUE_DESTROY, - &vdp_presentation_queue_destroy}, - {VDP_FUNC_ID_PRESENTATION_QUEUE_DISPLAY, - &vdp_presentation_queue_display}, - {VDP_FUNC_ID_PRESENTATION_QUEUE_BLOCK_UNTIL_SURFACE_IDLE, - &vdp_presentation_queue_block_until_surface_idle}, - {VDP_FUNC_ID_PRESENTATION_QUEUE_TARGET_CREATE_X11, - &vdp_presentation_queue_target_create_x11}, - {VDP_FUNC_ID_OUTPUT_SURFACE_RENDER_OUTPUT_SURFACE, - &vdp_output_surface_render_output_surface}, - {VDP_FUNC_ID_OUTPUT_SURFACE_PUT_BITS_INDEXED, - &vdp_output_surface_put_bits_indexed}, - {VDP_FUNC_ID_DECODER_CREATE, &vdp_decoder_create}, - {VDP_FUNC_ID_DECODER_RENDER, &vdp_decoder_render}, - {VDP_FUNC_ID_DECODER_DESTROY, &vdp_decoder_destroy}, - {VDP_FUNC_ID_BITMAP_SURFACE_CREATE, &vdp_bitmap_surface_create}, - {VDP_FUNC_ID_BITMAP_SURFACE_DESTROY, &vdp_bitmap_surface_destroy}, - {VDP_FUNC_ID_BITMAP_SURFACE_PUT_BITS_NATIVE, - &vdp_bitmap_surface_putbits_native}, - {VDP_FUNC_ID_OUTPUT_SURFACE_RENDER_BITMAP_SURFACE, - &vdp_output_surface_render_bitmap_surface}, - {VDP_FUNC_ID_GENERATE_CSC_MATRIX, &vdp_generate_csc_matrix}, - {VDP_FUNC_ID_PREEMPTION_CALLBACK_REGISTER, - &vdp_preemption_callback_register}, - {0, NULL} +#define VDP_FUNCTION(_, macro_name, mp_name) {macro_name, offsetof(struct vdp_functions, mp_name)}, +#include "vdpau_template.c" +#undef VDP_FUNCTION + {0, -1} }; - vdp_st = vdp_device_create_x11(mDisplay, mScreen, - &vdp_device, &vdp_get_proc_address); + vdp_st = vdp_device_create_x11(x11->display, x11->screen,&vc->vdp_device, + &vc->vdp_get_proc_address); if (vdp_st != VDP_STATUS_OK) { - mp_msg(MSGT_VO, MSGL_ERR, "[vdpau] Error when calling vdp_device_create_x11: %i\n", vdp_st); + mp_msg(MSGT_VO, MSGL_ERR, "[vdpau] Error when calling " + "vdp_device_create_x11: %i\n", vdp_st); return -1; } - vdp_get_error_string = NULL; - for (dsc = vdp_func; dsc->pointer; dsc++) { - vdp_st = vdp_get_proc_address(vdp_device, dsc->id, dsc->pointer); + vdp->get_error_string = NULL; + for (dsc = vdp_func; dsc->offset >= 0; dsc++) { + vdp_st = vc->vdp_get_proc_address(vc->vdp_device, dsc->id, + (void **)((char *)vdp + dsc->offset)); if (vdp_st != VDP_STATUS_OK) { - mp_msg(MSGT_VO, MSGL_ERR, "[vdpau] Error when calling vdp_get_proc_address(function id %d): %s\n", - dsc->id, vdp_get_error_string ? vdp_get_error_string(vdp_st) : "?"); + mp_msg(MSGT_VO, MSGL_ERR, "[vdpau] Error when calling " + "vdp_get_proc_address(function id %d): %s\n", dsc->id, + vdp->get_error_string ? vdp->get_error_string(vdp_st) : "?"); return -1; } } - vdp_st = vdp_preemption_callback_register(vdp_device, - preemption_callback, NULL); - CHECK_ST_ERROR("Error when calling vdp_preemption_callback_register") - + vdp_st = vdp->preemption_callback_register(vc->vdp_device, + preemption_callback, vc); return 0; } -/* Initialize vdpau_flip_queue, called from config() */ -static int win_x11_init_vdpau_flip_queue(void) +static int win_x11_init_vdpau_flip_queue(struct vo *vo) { + struct vdpctx *vc = vo->priv; + struct vdp_functions *vdp = vc->vdp; + struct vo_x11_state *x11 = vo->x11; VdpStatus vdp_st; - vdp_st = vdp_presentation_queue_target_create_x11(vdp_device, vo_window, - &vdp_flip_target); - CHECK_ST_ERROR("Error when calling vdp_presentation_queue_target_create_x11") + if (vc->flip_target == VDP_INVALID_HANDLE) { + vdp_st = vdp->presentation_queue_target_create_x11(vc->vdp_device, + x11->window, + &vc->flip_target); + CHECK_ST_ERROR("Error when calling " + "vdp_presentation_queue_target_create_x11"); + } + + /* Emperically this seems to be the first call which fails when we + * try to reinit after preemption while the user is still switched + * from X to a virtual terminal (creating the vdp_device initially + * succeeds, as does creating the flip_target above). This is + * probably not guaranteed behavior, but we'll assume it as a simple + * way to reduce warnings while trying to recover from preemption. + */ + if (vc->flip_queue == VDP_INVALID_HANDLE) { + vdp_st = vdp->presentation_queue_create(vc->vdp_device, vc->flip_target, + &vc->flip_queue); + if (vc->is_preempted && vdp_st != VDP_STATUS_OK) { + mp_msg(MSGT_VO, MSGL_DBG2, "[vdpau] Failed to create flip queue " + "while preempted: %s\n", vdp->get_error_string(vdp_st)); + return -1; + } else + CHECK_ST_ERROR("Error when calling vdp_presentation_queue_create"); + } - vdp_st = vdp_presentation_queue_create(vdp_device, vdp_flip_target, - &vdp_flip_queue); - CHECK_ST_ERROR("Error when calling vdp_presentation_queue_create") + VdpTime vdp_time; + vdp_st = vdp->presentation_queue_get_time(vc->flip_queue, &vdp_time); + CHECK_ST_ERROR("Error when calling vdp_presentation_queue_get_time"); + vc->last_vdp_time = vdp_time; + vc->last_sync_update = GetTimer(); + + vc->vsync_interval = 1; + if (vc->user_fps > 0) { + vc->vsync_interval = 1e9 / vc->user_fps; + mp_msg(MSGT_VO, MSGL_INFO, "[vdpau] Assuming user-specified display " + "refresh rate of %.3f Hz.\n", vc->user_fps); + } else if (vc->user_fps == 0) { +#ifdef CONFIG_XF86VM + double fps = vo_vm_get_fps(vo); + if (!fps) + mp_msg(MSGT_VO, MSGL_WARN, "[vdpau] Failed to get display FPS\n"); + else { + vc->vsync_interval = 1e9 / fps; + // This is verbose, but I'm not yet sure how common wrong values are + mp_msg(MSGT_VO, MSGL_INFO, + "[vdpau] Got display refresh rate %.3f Hz.\n" + "[vdpau] If that value looks wrong give the " + "-vo vdpau:fps=X suboption manually.\n", fps); + } +#else + mp_msg(MSGT_VO, MSGL_INFO, "[vdpau] This binary has been compiled " + "without XF86VidMode support.\n"); + mp_msg(MSGT_VO, MSGL_INFO, "[vdpau] Can't use vsync-aware timing " + "without manually provided -vo vdpau:fps=X suboption.\n"); +#endif + } else + mp_msg(MSGT_VO, MSGL_V, "[vdpau] framedrop/timing logic disabled by " + "user.\n"); return 0; } -static int update_csc_matrix(void) +static int set_video_attribute(struct vdpctx *vc, VdpVideoMixerAttribute attr, + const void *value, char *attr_name) { + struct vdp_functions *vdp = vc->vdp; VdpStatus vdp_st; - VdpCSCMatrix matrix; - static const VdpVideoMixerAttribute attributes[] = {VDP_VIDEO_MIXER_ATTRIBUTE_CSC_MATRIX}; - const void *attribute_values[] = {&matrix}; - static const VdpColorStandard vdp_colors[] = {0, VDP_COLOR_STANDARD_ITUR_BT_601, VDP_COLOR_STANDARD_ITUR_BT_709, VDP_COLOR_STANDARD_SMPTE_240M}; - static const char * const vdp_names[] = {NULL, "BT.601", "BT.709", "SMPTE-240M"}; - int csp = colorspace; - if (!csp) - csp = vid_width >= 1280 || vid_height > 576 ? 2 : 1; + vdp_st = vdp->video_mixer_set_attribute_values(vc->video_mixer, 1, &attr, + &value); + if (vdp_st != VDP_STATUS_OK) { + mp_msg(MSGT_VO, MSGL_ERR, "[vdpau] Error setting video mixer " + "attribute %s: %s\n", attr_name, vdp->get_error_string(vdp_st)); + return -1; + } + return 0; +} + +static void update_csc_matrix(struct vo *vo) +{ + struct vdpctx *vc = vo->priv; + struct vdp_functions *vdp = vc->vdp; + VdpStatus vdp_st; + const VdpColorStandard vdp_colors[] = {VDP_COLOR_STANDARD_ITUR_BT_601, + VDP_COLOR_STANDARD_ITUR_BT_709, + VDP_COLOR_STANDARD_SMPTE_240M}; + char * const vdp_names[] = {"BT.601", "BT.709", "SMPTE-240M"}; + int csp = vc->colorspace; mp_msg(MSGT_VO, MSGL_V, "[vdpau] Updating CSC matrix for %s\n", vdp_names[csp]); - vdp_st = vdp_generate_csc_matrix(&procamp, vdp_colors[csp], &matrix); - CHECK_ST_WARNING("Error when generating CSC matrix") + VdpCSCMatrix matrix; + vdp_st = vdp->generate_csc_matrix(&vc->procamp, vdp_colors[csp], &matrix); + CHECK_ST_WARNING("Error when generating CSC matrix"); - vdp_st = vdp_video_mixer_set_attribute_values(video_mixer, 1, attributes, - attribute_values); - CHECK_ST_WARNING("Error when setting CSC matrix") - return VO_TRUE; + set_video_attribute(vc, VDP_VIDEO_MIXER_ATTRIBUTE_CSC_MATRIX, + &matrix, "CSC matrix"); } -static int create_vdp_mixer(VdpChromaType vdp_chroma_type) |