aboutsummaryrefslogtreecommitdiffhomepage
path: root/libvo/vo_vdpau.c
diff options
context:
space:
mode:
Diffstat (limited to 'libvo/vo_vdpau.c')
-rw-r--r--libvo/vo_vdpau.c2274
1 files changed, 1335 insertions, 939 deletions
diff --git a/libvo/vo_vdpau.c b/libvo/vo_vdpau.c
index 97242d9abf..484b5fc87d 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,623 +21,816 @@
* 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 <dlfcn.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.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 2
+#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 deint_buffer_past_frames;
-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;
+ VdpDeviceCreateX11 *vdp_device_create;
+ VdpGetProcAddress *vdp_get_proc_address;
+
+ VdpPresentationQueueTarget flip_target;
+ VdpPresentationQueue flip_queue;
+ uint64_t last_vdp_time;
+ unsigned int last_sync_update;
+
+ void *vdpau_lib_handle;
+
+ /* 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 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);
+ 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 = vc->vdp_device_create(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 void update_csc_matrix(struct vo *vo)
{
+ struct vdpctx *vc = vo->priv;
+ 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;
+ 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;
+ const VdpVideoMixerAttribute attributes[] =
+ {VDP_VIDEO_MIXER_ATTRIBUTE_CSC_MATRIX};
+ const void *attribute_values[] = {&matrix};
+ vdp_st = vdp->video_mixer_set_attribute_values(vc->video_mixer, 1,
+ attributes,
+ attribute_values);
+ CHECK_ST_WARNING("Error when setting CSC matrix");
}
-static int create_vdp_mixer(VdpChromaType vdp_chroma_type)
+static int create_vdp_mixer(struct vo *vo, VdpChromaType vdp_chroma_type)
{
+ struct vdpctx *vc = vo->priv;
+ struct vdp_functions *vdp = vc->vdp;
#define VDP_NUM_MIXER_PARAMETER 3
#define MAX_NUM_FEATURES 6
int i;
VdpStatus vdp_st;
+
+ if (vc->video_mixer != VDP_INVALID_HANDLE)
+ return 0;
+
int feature_count = 0;
VdpVideoMixerFeature features[MAX_NUM_FEATURES];
VdpBool feature_enables[MAX_NUM_FEATURES];
- static const VdpVideoMixerAttribute denoise_attrib[] = {VDP_VIDEO_MIXER_ATTRIBUTE_NOISE_REDUCTION_LEVEL};
- const void * const denoise_value[] = {&denoise};
- static const VdpVideoMixerAttribute sharpen_attrib[] = {VDP_VIDEO_MIXER_ATTRIBUTE_SHARPNESS_LEVEL};
- const void * const sharpen_value[] = {&sharpen};
- static const VdpVideoMixerAttribute skip_chroma_attrib[] = {VDP_VIDEO_MIXER_ATTRIBUTE_SKIP_CHROMA_DEINTERLACE};
+ static const VdpVideoMixerAttribute denoise_attrib[] =
+ {VDP_VIDEO_MIXER_ATTRIBUTE_NOISE_REDUCTION_LEVEL};
+ const void * const denoise_value[] = {&vc->denoise};
+ static const VdpVideoMixerAttribute sharpen_attrib[] =
+ {VDP_VIDEO_MIXER_ATTRIBUTE_SHARPNESS_LEVEL};
+ const void * const sharpen_value[] = {&vc->sharpen};
+ static const VdpVideoMixerAttribute skip_chroma_attrib[] =
+ {VDP_VIDEO_MIXER_ATTRIBUTE_SKIP_CHROMA_DEINTERLACE};
const uint8_t skip_chroma_value = 1;
const void * const skip_chroma_value_ptr[] = {&skip_chroma_value};
static const VdpVideoMixerParameter parameters[VDP_NUM_MIXER_PARAMETER] = {
VDP_VIDEO_MIXER_PARAMETER_VIDEO_SURFACE_WIDTH,
VDP_VIDEO_MIXER_PARAMETER_VIDEO_SURFACE_HEIGHT,
- VDP_VIDEO_MIXER_PARAMETER_CHROMA_TYPE
+ VDP_VIDEO_MIXER_PARAMETER_CHROMA_TYPE,
};
const void *const parameter_values[VDP_NUM_MIXER_PARAMETER] = {
- &vid_width,
- &vid_height,
- &vdp_chroma_type
+ &vc->vid_width,
+ &vc->vid_height,
+ &vdp_chroma_type,
};
features[feature_count++] = VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL;
- if (deint == 4)
- features[feature_count++] = VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL_SPATIAL;
- if (pullup)
+ if (vc->deint == 4)
+ features[feature_count++] =
+ VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL_SPATIAL;
+ if (vc->pullup)
features[feature_count++] = VDP_VIDEO_MIXER_FEATURE_INVERSE_TELECINE;
- if (denoise)
+ if (vc->denoise)
features[feature_count++] = VDP_VIDEO_MIXER_FEATURE_NOISE_REDUCTION;
- if (sharpen)
+ if (vc->sharpen)
features[feature_count++] = VDP_VIDEO_MIXER_FEATURE_SHARPNESS;
- if (hqscaling)
- features[feature_count++] = VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L1 + (hqscaling - 1);
+ if (vc->hqscaling) {
+#ifndef VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L1
+ mp_msg(MSGT_VO, MSGL_ERR, "[vdpau] MPlayer was compiled with (old?)"
+ "libvdpau headers with no support for requested hqscaling.\n");
+#else
+ VdpVideoMixerFeature hqscaling_feature =
+ VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L1 + vc->hqscaling-1;
+ VdpBool hqscaling_available;
+ vdp_st = vdp->video_mixer_query_feature_support(vc->vdp_device,
+ hqscaling_feature,
+ &hqscaling_available);
+ CHECK_ST_ERROR("Error when calling video_mixer_query_feature_support");
+ if (hqscaling_available)
+ features[feature_count++] = hqscaling_feature;
+ else
+ mp_msg(MSGT_VO, MSGL_ERR, "[vdpau] Your hardware or VDPAU "
+ "library does not support requested hqscaling.\n");
+ }
+#endif
- vdp_st = vdp_video_mixer_create(vdp_device, feature_count, features,
- VDP_NUM_MIXER_PARAMETER,
- parameters, parameter_values,
- &video_mixer);
- CHECK_ST_ERROR("Error when calling vdp_video_mixer_create")
+ vdp_st = vdp->video_mixer_create(vc->vdp_device, feature_count, features,
+ VDP_NUM_MIXER_PARAMETER,
+ parameters, parameter_values,
+ &vc->video_mixer);
+ CHECK_ST_ERROR("Error when calling vdp_video_mixer_create");
for (i = 0; i < feature_count; i++)
feature_enables[i] = VDP_TRUE;
- if (deint < 3)
+ if (vc->deint < 3)
feature_enables[0] = VDP_FALSE;
if (feature_count)
- vdp_video_mixer_set_feature_enables(video_mixer, feature_count, features, feature_enables);
- if (denoise)
- vdp_video_mixer_set_attribute_values(video_mixer, 1, denoise_attrib, denoise_value);
- if (sharpen)
- vdp_video_mixer_set_attribute_values(video_mixer, 1, sharpen_attrib, sharpen_value);
- if (!chroma_deint)
- vdp_video_mixer_set_attribute_values(video_mixer, 1, skip_chroma_attrib, skip_chroma_value_ptr);
-
- update_csc_matrix();
+ vdp->video_mixer_set_feature_enables(vc->video_mixer, feature_count,
+ features, feature_enables);
+ if (vc->denoise)
+ vdp->video_mixer_set_attribute_values(vc->video_mixer, 1,
+ denoise_attrib, denoise_value);
+ if (vc->sharpen)
+ vdp->video_mixer_set_attribute_values(vc->video_mixer, 1,
+ sharpen_attrib, sharpen_value);
+ if (!vc->chroma_deint)
+ vdp->video_mixer_set_attribute_values(vc->video_mixer, 1,
+ skip_chroma_attrib,
+ skip_chroma_value_ptr);
+
+ update_csc_matrix(vo);
return 0;
}
// Free everything specific to a certain video file
-static void free_video_specific(void)
+static void free_video_specific(struct vo *vo)
{
+ struct vdpctx *vc = vo->priv;
+ struct vdp_functions *vdp = vc->vdp;
int i;
VdpStatus vdp_st;
- if (decoder != VDP_INVALID_HANDLE)
- vdp_decoder_destroy(decoder);
- decoder = VDP_INVALID_HANDLE;
- decoder_max_refs = -1;
+ if (vc->decoder != VDP_INVALID_HANDLE)
+ vdp->decoder_destroy(vc->decoder);
+ vc->decoder = VDP_INVALID_HANDLE;
+ vc->decoder_max_refs = -1;
- for (i = 0; i < 3; i++)
- deint_surfaces[i] = VDP_INVALID_HANDLE;
-
- for (i = 0; i < 2; i++)
- if (deint_mpi[i]) {
- deint_mpi[i]->usage_count--;
- deint_mpi[i] = NULL;
- }
+ forget_frames(vo);
for (i = 0; i < MAX_VIDEO_SURFACES; i++) {
- if (surface_render[i].surface != VDP_INVALID_HANDLE) {
- vdp_st = vdp_video_surface_destroy(surface_render[i].surface);
- CHECK_ST_WARNING("Error when calling vdp_video_surface_destroy")
+ if (vc->surface_render[i].surface != VDP_INVALID_HANDLE) {
+ vdp_st = vdp->video_surface_destroy(vc->surface_render[i].surface);
+ CHECK_ST_WARNING("Error when calling vdp_video_surface_destroy");
}
- surface_render[i].surface = VDP_INVALID_HANDLE;
+ vc->surface_render[i].surface = VDP_INVALID_HANDLE;
}
- if (video_mixer != VDP_INVALID_HANDLE) {
- vdp_st = vdp_video_mixer_destroy(video_mixer);
- CHECK_ST_WARNING("Error when calling vdp_video_mixer_destroy")
+ if (vc->video_mixer != VDP_INVALID_HANDLE) {
+ vdp_st = vdp->video_mixer_destroy(vc->video_mixer);
+ CHECK_ST_WARNING("Error when calling vdp_video_mixer_destroy");
}
- video_mixer = VDP_INVALID_HANDLE;
+ vc->video_mixer = VDP_INVALID_HANDLE;
}
-static int create_vdp_decoder(uint32_t format, uint32_t width, uint32_t height,
- int max_refs)
+static int create_vdp_decoder(struct vo *vo, int max_refs)
{
+ struct vdpctx *vc = vo->priv;
+ struct vdp_functions *vdp = vc->vdp;
VdpStatus vdp_st;
VdpDecoderProfile vdp_decoder_profile;
- if (decoder != VDP_INVALID_HANDLE)
- vdp_decoder_destroy(decoder);
- switch (format) {
- case IMGFMT_VDPAU_MPEG1:
- vdp_decoder_profile = VDP_DECODER_PROFILE_MPEG1;
- break;
- case IMGFMT_VDPAU_MPEG2:
- vdp_decoder_profile = VDP_DECODER_PROFILE_MPEG2_MAIN;
- break;
- case IMGFMT_VDPAU_H264:
- vdp_decoder_profile = VDP_DECODER_PROFILE_H264_HIGH;
- mp_msg(MSGT_VO, MSGL_V, "[vdpau] Creating H264 hardware decoder for %d reference frames.\n", max_refs);
- break;
- case IMGFMT_VDPAU_WMV3:
- vdp_decoder_profile = VDP_DECODER_PROFILE_VC1_MAIN;
- break;
- case IMGFMT_VDPAU_VC1:
- vdp_decoder_profile = VDP_DECODER_PROFILE_VC1_ADVANCED;
- break;
- case IMGFMT_VDPAU_MPEG4:
- vdp_decoder_profile = VDP_DECODER_PROFILE_MPEG4_PART2_ASP;
- break;
- default:
- goto err_out;
+ if (vc->decoder != VDP_INVALID_HANDLE)
+ vdp->decoder_destroy(vc->decoder);
+ switch (vc->image_format) {
+ case IMGFMT_VDPAU_MPEG1:
+ vdp_decoder_profile = VDP_DECODER_PROFILE_MPEG1;
+ break;
+ case IMGFMT_VDPAU_MPEG2:
+ vdp_decoder_profile = VDP_DECODER_PROFILE_MPEG2_MAIN;
+ break;
+ case IMGFMT_VDPAU_H264:
+ vdp_decoder_profile = VDP_DECODER_PROFILE_H264_HIGH;
+ mp_msg(MSGT_VO, MSGL_V, "[vdpau] Creating H264 hardware decoder "
+ "for %d reference frames.\n", max_refs);
+ break;
+ case IMGFMT_VDPAU_WMV3:
+ vdp_decoder_profile = VDP_DECODER_PROFILE_VC1_MAIN;
+ break;
+ case IMGFMT_VDPAU_VC1:
+ vdp_decoder_profile = VDP_DECODER_PROFILE_VC1_ADVANCED;
+ break;
+#ifdef VDP_DECODER_PROFILE_MPEG4_PART2_ASP
+ case IMGFMT_VDPAU_MPEG4:
+ vdp_decoder_profile = VDP_DECODER_PROFILE_MPEG4_PART2_ASP;
+ break;
+#endif
}
- vdp_st = vdp_decoder_create(vdp_device, vdp_decoder_profile,
- width, height, max_refs, &decoder);
+ vdp_st = vdp->decoder_create(vc->vdp_device, vdp_decoder_profile,
+ vc->vid_width, vc->vid_height, max_refs,
+ &vc->decoder);
CHECK_ST_WARNING("Failed creating VDPAU decoder");
if (vdp_st != VDP_STATUS_OK) {
-err_out:
- decoder = VDP_INVALID_HANDLE;
- decoder_max_refs = 0;
+ vc->decoder = VDP_INVALID_HANDLE;
+ vc->decoder_max_refs = 0;
return 0;
}
- decoder_max_refs = max_refs;
+ vc->decoder_max_refs = max_refs;
return 1;
}
-static void mark_vdpau_objects_uninitialized(void)
+static int initialize_vdpau_objects(struct vo *vo)
{
- int i;
+ struct vdpctx *vc = vo->priv;
+ struct vdp_functions *vdp = vc->vdp;
+ VdpStatus vdp_st;
- decoder = VDP_INVALID_HANDLE;
- for (i = 0; i < MAX_VIDEO_SURFACES; i++)
- surface_render[i].surface = VDP_INVALID_HANDLE;
- for (i = 0; i < 3; i++) {
- deint_surfaces[i] = VDP_INVALID_HANDLE;
- if (i < 2 && deint_mpi[i])
- deint_mpi[i]->usage_count--;
- deint_mpi[i] = NULL;
+ vc->vdp_chroma_type = VDP_CHROMA_TYPE_420;
+ switch (vc->image_format) {
+ case IMGFMT_YV12:
+ case IMGFMT_I420:
+ case IMGFMT_IYUV:
+ vc->vdp_pixel_format = VDP_YCBCR_FORMAT_YV12;
+ break;
+ case IMGFMT_NV12:
+ vc->vdp_pixel_format = VDP_YCBCR_FORMAT_NV12;
+ break;
+ case IMGFMT_YUY2:
+ vc->vdp_pixel_format = VDP_YCBCR_FORMAT_YUYV;
+ vc->vdp_chroma_type = VDP_CHROMA_TYPE_422;
+ break;
+ case IMGFMT_UYVY:
+ vc->vdp_pixel_format = VDP_YCBCR_FORMAT_UYVY;
+ vc->vdp_chroma_type = VDP_CHROMA_TYPE_422;
}
- video_mixer = VDP_INVALID_HANDLE;
- vdp_flip_queue = VDP_INVALID_HANDLE;
- vdp_flip_target = VDP_INVALID_HANDLE;
- for (i = 0; i <= NUM_OUTPUT_SURFACES; i++)
- output_surfaces[i] = VDP_INVALID_HANDLE;
- vdp_device = VDP_INVALID_HANDLE;
- for (i = 0; i < eosd_surface_count; i++)
- eosd_surfaces[i].surface = VDP_INVALID_HANDLE;
- output_surface_width = output_surface_height = -1;
- eosd_render_count = 0;
- visible_buf = 0;
+ if (win_x11_init_vdpau_flip_queue(vo) < 0)
+ return -1;
+
+ if (create_vdp_mixer(vo, vc->vdp_chroma_type) < 0)
+ return -1;
+
+ vdp_st = vdp->
+ bitmap_surface_query_capabilities(vc->vdp_device,
+ VDP_RGBA_FORMAT_A8,
+ &(VdpBool){0},
+ &vc->eosd_surface.max_width,
+ &vc->eosd_surface.max_height);
+ CHECK_ST_WARNING("Query to get max EOSD surface size failed");
+ forget_frames(vo);
+ resize(vo);
+ return 0;
}
-static int handle_preemption(void)
+static void mark_vdpau_objects_uninitialized(struct vo *vo)
{
- if (!is_preempted)
+ struct vdpctx *vc = vo->priv;
+
+ vc->decoder = VDP_INVALID_HANDLE;
+ for (int i = 0; i < MAX_VIDEO_SURFACES; i++)
+ vc->surface_render[i].surface = VDP_INVALID_HANDLE;
+ forget_frames(vo);
+ vc->video_mixer = VDP_INVALID_HANDLE;
+ vc->flip_queue = VDP_INVALID_HANDLE;
+ vc->flip_target = VDP_INVALID_HANDLE;
+ for (int i = 0; i <= NUM_OUTPUT_SURFACES; i++)
+ vc->output_surfaces[i] = VDP_INVALID_HANDLE;
+ vc->vdp_device = VDP_INVALID_HANDLE;
+ vc->eosd_surface = (struct eosd_bitmap_surface){
+ .surface = VDP_INVALID_HANDLE,
+ };
+ vc->output_surface_width = vc->output_surface_height = -1;
+ vc->eosd_render_count = 0;
+ vc->num_shown_frames = 0;
+}
+
+static int handle_preemption(struct vo *vo)
+{
+ struct vdpctx *vc = vo->priv;
+
+ if (!vc->is_preempted)
return 0;
- is_preempted = 0;
- mp_msg(MSGT_VO, MSGL_INFO, "[vdpau] Attempting to recover from preemption.\n");
- mark_vdpau_objects_uninitialized();
- if (win_x11_init_vdpau_procs() < 0 ||
- win_x11_init_vdpau_flip_queue() < 0 ||
- create_vdp_mixer(vdp_chroma_type) < 0) {
- mp_msg(MSGT_VO, MSGL_ERR, "[vdpau] Recovering from preemption failed\n");
- is_preempted = 1;
+ if (!vc->preemption_acked)
+ mark_vdpau_objects_uninitialized(vo);
+ vc->preemption_acked = true;
+ if (!vc->preemption_user_notified) {
+ mp_tmsg(MSGT_VO, MSGL_ERR, "[vdpau] Got display preemption notice! "
+ "Will attempt to recover.\n");
+ vc->preemption_user_notified = true;
+ }
+ /* Trying to initialize seems to be quite slow, so only try once a
+ * second to avoid using 100% CPU. */
+ if (vc->last_preemption_retry_fail
+ && GetTimerMS() - vc->last_preemption_retry_fail < 1000)
+ return -1;
+ if (win_x11_init_vdpau_procs(vo) < 0 || initialize_vdpau_objects(vo) < 0) {
+ vc->last_preemption_retry_fail = GetTimerMS() | 1;
return -1;
}
- resize();
- mp_msg(MSGT_VO, MSGL_INFO, "[vdpau] Recovered from display preemption.\n");
+ vc->last_preemption_retry_fail = 0;
+ vc->is_preempted = false;
+ vc->preemption_user_notified = false;
+ mp_tmsg(MSGT_VO, MSGL_INFO, "[vdpau] Recovered from display preemption.\n");
return 1;
}
@@ -644,10 +838,12 @@ static int handle_preemption(void)
* connect to X server, create and map window, initialize all
* VDPAU objects, create different surfaces etc.
*/
-static int config(uint32_t width, uint32_t height, uint32_t d_width,
- uint32_t d_height, uint32_t flags, char *title,
- uint32_t format)
+static int config(struct vo *vo, uint32_t width, uint32_t height,
+ uint32_t d_width, uint32_t d_height, uint32_t flags,
+ char *title, uint32_t format)
{
+ struct vdpctx *vc = vo->priv;
+ struct vo_x11_state *x11 = vo->x11;
XVisualInfo vinfo;
XSetWindowAttributes xswa;
XWindowAttributes attribs;
@@ -657,142 +853,121 @@ static int config(uint32_t width, uint32_t height, uint32_t d_width,
#ifdef CONFIG_XF86VM
int vm = flags & VOFLAG_MODESWITCHING;
#endif
- flip = flags & VOFLAG_FLIPPING;
-
- image_format = format;
- vid_width = width;
- vid_height = height;
- free_video_specific();
- if (IMGFMT_IS_VDPAU(image_format)
- && !create_vdp_decoder(image_format, vid_width, vid_height, 2))
- return -1;
- int_pause = 0;
- visible_buf = 0;
+ if (handle_preemption(vo) < 0)
+ return -1;
-#ifdef CONFIG_GUI
- if (use_gui)
- guiGetEvent(guiSetShVideo, 0); // the GUI will set up / resize our window
+ vc->flip = flags & VOFLAG_FLIPPING;
+ vc->image_format = format;
+ vc->vid_width = width;
+ vc->vid_height = height;
+ if (vc->user_colorspace == 0)
+ vc->colorspace = width >= 1280 || height > 576 ? 1 : 0;
else
-#endif
- {
+ vc->colorspace = vc->user_colorspace - 1;
+ free_video_specific(vo);
+ if (IMGFMT_IS_VDPAU(vc->image_format) && !create_vdp_decoder(vo, 2))
+ return -1;
+
#ifdef CONFIG_XF86VM
- if (vm)
- vo_vm_switch();
- else
+ if (vm) {
+ vo_vm_switch(vo);
+ vc->mode_switched = true;
+ }
#endif
- XGetWindowAttributes(mDisplay, DefaultRootWindow(mDisplay), &attribs);
- depth = attribs.depth;
- if (depth != 15 && depth != 16 && depth != 24 && depth != 32)
- depth = 24;
- XMatchVisualInfo(mDisplay, mScreen, depth, TrueColor, &vinfo);
-
- xswa.background_pixel = 0;
- xswa.border_pixel = 0;
- /* Do not use CWBackPixel: It leads to VDPAU errors after
- aspect ratio changes. */
- xswamask = CWBorderPixel;
-
- vo_x11_create_vo_window(&vinfo, vo_dx, vo_dy, d_width, d_height,
- flags, CopyFromParent, "vdpau", title);
- XChangeWindowAttributes(mDisplay, vo_window, xswamask, &xswa);
+ XGetWindowAttributes(x11->display, DefaultRootWindow(x11->display),
+ &attribs);
+ depth = attribs.depth;
+ if (depth != 15 && depth != 16 && depth != 24 && depth != 32)
+ depth = 24;
+ XMatchVisualInfo(x11->display, x11->screen, depth, TrueColor, &vinfo);
+
+ xswa.background_pixel = 0;
+ xswa.border_pixel = 0;
+ /* Do not use CWBackPixel: It leads to VDPAU errors after
+ * aspect ratio changes. */
+ xswamask = CWBorderPixel;
+
+ vo_x11_create_vo_window(vo, &vinfo, vo->dx, vo->dy, d_width, d_height,
+ flags, CopyFromParent, "vdpau", title);
+ XChangeWindowAttributes(x11->display, x11->window, xswamask, &xswa);
#ifdef CONFIG_XF86VM
- if (vm) {
- /* Grab the mouse pointer in our window */
- if (vo_grabpointer)
- XGrabPointer(mDisplay, vo_window, True, 0,
- GrabModeAsync, GrabModeAsync,
- vo_window, None, CurrentTime);
- XSetInputFocus(mDisplay, vo_window, RevertToNone, CurrentTime);
- }
-#endif
+ if (vm) {
+ /* Grab the mouse pointer in our window */
+ if (vo_grabpointer)
+ XGrabPointer(x11->display, x11->window, True, 0,
+ GrabModeAsync, GrabModeAsync,
+ x11->window, None, CurrentTime);
+ XSetInputFocus(x11->display, x11->window, RevertToNone, CurrentTime);
}
+#endif
if ((flags & VOFLAG_FULLSCREEN) && WinID <= 0)
vo_fs = 1;
- /* -----VDPAU related code here -------- */
- if (vdp_flip_queue == VDP_INVALID_HANDLE && win_x11_init_vdpau_flip_queue())
+ if (initialize_vdpau_objects(vo) < 0)
return -1;
- vdp_chroma_type = VDP_CHROMA_TYPE_420;
- switch (image_format) {
- case IMGFMT_YV12:
- case IMGFMT_I420:
- case IMGFMT_IYUV:
- vdp_pixel_format = VDP_YCBCR_FORMAT_YV12;
- break;
- case IMGFMT_NV12:
- vdp_pixel_format = VDP_YCBCR_FORMAT_NV12;
- break;
- case IMGFMT_YUY2:
- vdp_pixel_format = VDP_YCBCR_FORMAT_YUYV;
- vdp_chroma_type = VDP_CHROMA_TYPE_422;
- break;
- case IMGFMT_UYVY:
- vdp_pixel_format = VDP_YCBCR_FORMAT_UYVY;
- vdp_chroma_type = VDP_CHROMA_TYPE_422;
- }
- if (create_vdp_mixer(vdp_chroma_type))
- return -1;
-
- surface_num = 0;
- vid_surface_num = -1;
- resize();
-
return 0;
}
-static void check_events(void)
+static void check_events(struct vo *vo)
{
- int e = vo_x11_check_events(mDisplay);
+ struct vdpctx *vc = vo->priv;
+ struct vdp_functions *vdp = vc->vdp;
- if (handle_preemption() < 0)
+ if (handle_preemption(vo) < 0)
return;
- if (e & VO_EVENT_RESIZE)
- resize();
+ int e = vo_x11_check_events(vo);
- if ((e & VO_EVENT_EXPOSE || e & VO_EVENT_RESIZE) && int_pause) {
+ if (e & VO_EVENT_RESIZE)
+ resize(vo);
+ else if (e & VO_EVENT_EXPOSE && vc->paused) {
/* did we already draw a buffer */
- if (visible_buf) {
+ if (vc->num_shown_frames) {
/* redraw the last visible buffer */
VdpStatus vdp_st;
- vdp_st = vdp_presentation_queue_display(vdp_flip_queue,
- output_surfaces[surface_num],
- vo_dwidth, vo_dheight,
- 0);
- CHECK_ST_WARNING("Error when calling vdp_presentation_queue_display")
+ int last_surface = (vc->surface_num + NUM_OUTPUT_SURFACES - 1)
+ % NUM_OUTPUT_SURFACES;
+ vdp_st = vdp->presentation_queue_display(vc->flip_queue,
+ vc->output_surfaces[last_surface],
+ vo->dwidth, vo->dheight, 0);
+ CHECK_ST_WARNING("Error when calling "
+ "vdp_presentation_queue_display");
}
}
}
-static void draw_osd_I8A8(int x0,int y0, int w,int h, unsigned char *src,
- unsigned char *srca, int stride)
+static void draw_osd_I8A8(void *ctx, int x0, int y0, int w, int h,
+ unsigned char *src, unsigned char *srca, int stride)
{
- VdpOutputSurface output_surface = output_surfaces[surface_num];
+ struct vo *vo = ctx;
+ struct vdpctx *vc = vo->priv;
+ struct vdp_functions *vdp = vc->vdp;
+ VdpOutputSurface output_surface = vc->output_surfaces[vc->surface_num];
VdpStatus vdp_st;
- int i, j;
+ int i;
int pitch;
int index_data_size_required;
VdpRect output_indexed_rect_vid;
- VdpOutputSurfaceRenderBlendState blend_state;
if (!w || !h)
return;
index_data_size_required = 2*w*h;
- if (index_data_size < index_data_size_required) {
- index_data = realloc(index_data, index_data_size_required);
- index_data_size = index_data_size_required;
+ if (vc->index_data_size < index_data_size_required) {
+ vc->index_data = talloc_realloc_size(vc, vc->index_data,
+ index_data_size_required);
+ vc->index_data_size = index_data_size_required;
}
// index_data creation, component order - I, A, I, A, .....
for (i = 0; i < h; i++)
- for (j = 0; j < w; j++) {
- index_data[i*2*w + j*2] = src [i*stride + j];
- index_data[i*2*w + j*2 + 1] = -srca[i*stride + j];
+ for (int j = 0; j < w; j++) {
+ vc->index_data[i*2*w + j*2] = src [i*stride+j];
+ vc->index_data[i*2*w + j*2 + 1] = -srca[i*stride+j];
}
output_indexed_rect_vid.x0 = x0;
@@ -803,283 +978,510 @@ static void draw_osd_I8A8(int x0,int y0, int w,int h, unsigned char *src,
pitch = w*2;
// write source_data to osd_surface.
- vdp_st = vdp_output_surface_put_bits_indexed(osd_surface,
- VDP_INDEXED_FORMAT_I8A8,
- (const void *const*)&index_data,
- &pitch,
- &output_indexed_rect_vid,
- VDP_COLOR_TABLE_FORMAT_B8G8R8X8,
- (void *)palette);
- CHECK_ST_WARNING("Error when calling vdp_output_surface_put_bits_indexed")
-
- blend_state.struct_version = VDP_OUTPUT_SURFACE_RENDER_BLEND_STATE_VERSION;
- blend_state.blend_factor_source_color = VDP_OUTPUT_SURFACE_RENDER_BLEND_FACTOR_ONE;
- blend_state.blend_factor_source_alpha = VDP_OUTPUT_SURFACE_RENDER_BLEND_FACTOR_ONE;
- blend_state.blend_factor_destination_color = VDP_OUTPUT_SURFACE_RENDER_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
- blend_state.blend_factor_destination_alpha = VDP_OUTPUT_SURFACE_RENDER_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
- blend_state.blend_equation_color = VDP_OUTPUT_SURFACE_RENDER_BLEND_EQUATION_ADD;
- blend_state.blend_equation_alpha = VDP_OUTPUT_SURFACE_RENDER_BLEND_EQUATION_ADD;
-
- vdp_st = vdp_output_surface_render_output_surface(output_surface,
- &output_indexed_rect_vid,
- osd_surface,
- &output_indexed_rect_vid,
- NULL,
- &blend_state,
- VDP_OUTPUT_SURFACE_RENDER_ROTATE_0);
- CHECK_ST_WARNING("Error when calling vdp_output_surface_render_output_surface")
+ vdp_st = vdp->
+ output_surface_put_bits_indexed(osd_surface, VDP_INDEXED_FORMAT_I8A8,
+ (const void *const*)&vc->index_data,
+ &pitch, &output_indexed_rect_vid,
+ VDP_COLOR_TABLE_FORMAT_B8G8R8X8,
+ (void *)vc->palette);
+ CHECK_ST_WARNING("Error when calling vdp_output_surface_put_bits_indexed");
+
+ VdpOutputSurfaceRenderBlendState blend_state = {
+ .struct_version = VDP_OUTPUT_SURFACE_RENDER_BLEND_STATE_VERSION,
+ .blend_factor_source_color =
+ VDP_OUTPUT_SURFACE_RENDER_BLEND_FACTOR_ONE,
+ .blend_factor_source_alpha =
+ VDP_OUTPUT_SURFACE_RENDER_BLEND_FACTOR_ONE,
+ .blend_factor_destination_color =
+ VDP_OUTPUT_SURFACE_RENDER_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA,
+ .blend_factor_destination_alpha =
+ VDP_OUTPUT_SURFACE_RENDER_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA,
+ .blend_equation_color = VDP_OUTPUT_SURFACE_RENDER_BLEND_EQUATION_ADD,
+ .blend_equation_alpha = VDP_OUTPUT_SURFACE_RENDER_BLEND_EQUATION_ADD,
+ };
+
+ vdp_st = vdp->
+ output_surface_render_output_surface(output_surface,
+ &output_indexed_rect_vid,
+ osd_surface,
+ &output_indexed_rect_vid,
+ NULL, &blend_state,
+ VDP_OUTPUT_SURFACE_RENDER_ROTATE_0);
+ CHECK_ST_WARNING("Error when calling "
+ "vdp_output_surface_render_output_surface");
}
-static void draw_eosd(void)
+static void draw_eosd(struct vo *vo)
{
+ struct vdpctx *vc = vo->priv;
+ struct vdp_functions *vdp = vc->vdp;
VdpStatus vdp_st;
- VdpOutputSurface output_surface = output_surfaces[surface_num];
- VdpOutputSurfaceRenderBlendState blend_state;
+ VdpOutputSurface output_surface = vc->output_surfaces[vc->surface_num];
int i;
- blend_state.struct_version = VDP_OUTPUT_SURFACE_RENDER_BLEND_STATE_VERSION;
- blend_state.blend_factor_source_color = VDP_OUTPUT_SURFACE_RENDER_BLEND_FACTOR_SRC_ALPHA;
- blend_state.blend_factor_source_alpha = VDP_OUTPUT_SURFACE_RENDER_BLEND_FACTOR_ONE;
- blend_state.blend_factor_destination_color = VDP_OUTPUT_SURFACE_RENDER_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
- blend_state.blend_factor_destination_alpha = VDP_OUTPUT_SURFACE_RENDER_BLEND_FACTOR_SRC_ALPHA;
- blend_state.blend_equation_color = VDP_OUTPUT_SURFACE_RENDER_BLEND_EQUATION_ADD;
- blend_state.blend_equation_alpha = VDP_OUTPUT_SURFACE_RENDER_BLEND_EQUATION_ADD;
-
- for (i = 0; i < eosd_render_count; i++) {
- vdp_st = vdp_output_surface_render_bitmap_surface(
- output_surface, &eosd_targets[i].dest,
- eosd_targets[i].surface, &eosd_targets[i].source,
- &eosd_targets[i].color, &blend_state,
- VDP_OUTPUT_SURFACE_RENDER_ROTATE_0);
- CHECK_ST_WARNING("EOSD: Error when rendering")
+ if (handle_preemption(vo) < 0)
+ return;
+
+ VdpOutputSurfaceRenderBlendState blend_state = {
+ .struct_version = VDP_OUTPUT_SURFACE_RENDER_BLEND_STATE_VERSION,
+ .blend_factor_source_color =
+ VDP_OUTPUT_SURFACE_RENDER_BLEND_FACTOR_SRC_ALPHA,
+ .blend_factor_source_alpha =
+ VDP_OUTPUT_SURFACE_RENDER_BLEND_FACTOR_ONE,
+ .blend_factor_destination_color =
+ VDP_OUTPUT_SURFACE_RENDER_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA,
+ .blend_factor_destination_alpha =
+ VDP_OUTPUT_SURFACE_RENDER_BLEND_FACTOR_SRC_ALPHA,
+ .blend_equation_color = VDP_OUTPUT_SURFACE_RENDER_BLEND_EQUATION_ADD,
+ .blend_equation_alpha = VDP_OUTPUT_SURFACE_RENDER_BLEND_EQUATION_ADD,
+ };
+
+ for (i = 0; i < vc->eosd_render_count; i++) {
+ vdp_st = vdp->
+ output_surface_render_bitmap_surface(output_surface,
+ &vc->eosd_targets[i].dest,
+ vc->eosd_surface.surface,
+ &vc->eosd_targets[i].source,
+ &vc->eosd_targets[i].color,
+ &blend_state,
+ VDP_OUTPUT_SURFACE_RENDER_ROTATE_0);
+ CHECK_ST_WARNING("EOSD: Error when rendering");
+ }
+}
+
+#define HEIGHT_SORT_BITS 4
+static int size_index(struct eosd_target *r)
+{
+ unsigned int h = r->source.y1;
+ int n = av_log2_16bit(h);
+ return (n << HEIGHT_SORT_BITS)
+ + (- 1 - (h << HEIGHT_SORT_BITS >> n) & (1 << HEIGHT_SORT_BITS) - 1);
+}
+
+/* Pack the given rectangles into an area of size w * h.
+ * The size of each rectangle is read from .source.x1/.source.y1.
+ * The height of each rectangle must be at least 1 and less than 65536.
+ * The .source rectangle is then set corresponding to the packed position.
+ * 'scratch' must point to work memory for num_rects+16 ints.
+ * Return 0 on success, -1 if the rectangles did not fit in w*h.
+ *
+ * The rectangles are placed in rows in order approximately sorted by
+ * height (the approximate sorting is simpler than a full one would be,
+ * and allows the algorithm to work in linear time). Additionally, to
+ * reduce wasted space when there are a few tall rectangles, empty
+ * lower-right parts of rows are filled recursively when the size of
+ * rectangles in the row drops past a power-of-two threshold. So if a
+ * row starts with rectangles of size 3x50, 10x40 and 5x20 then the
+ * free rectangle with corners (13, 20)-(w, 50) is filled recursively.
+ */
+static int pack_rectangles(struct eosd_target *rects, int num_rects,
+ int w, int h, int *scratch)
+{
+ int bins[16 << HEIGHT_SORT_BITS];
+ int sizes[16 << HEIGHT_SORT_BITS] = {};
+ for (int i = 0; i < num_rects; i++)
+ sizes[size_index(rects + i)]++;
+ int idx = 0;
+ for (int i = 0; i < 16 << HEIGHT_SORT_BITS; i += 1 << HEIGHT_SORT_BITS) {
+ for (int j = 0; j < 1 << HEIGHT_SORT_BITS; j++) {
+ bins[i + j] = idx;
+ idx += sizes[i + j];
+ }
+ scratch[idx++] = -1;
}
+ for (int i = 0; i < num_rects; i++)
+ scratch[bins[size_index(rects + i)]++] = i;
+ for (int i = 0; i < 16; i++)
+ bins[i] = bins[i << HEIGHT_SORT_BITS] - sizes[i << HEIGHT_SORT_BITS];
+ struct {
+ int size, x, bottom;
+ } stack[16] = {{15, 0, h}}, s = {};
+ int stackpos = 1;
+ int y;
+ while (stackpos) {
+ y = s.bottom;
+ s = stack[--stackpos];
+ s.size++;
+ while (s.size--) {
+ int maxy = -1;
+ int obj;
+ while ((obj = scratch[bins[s.size]]) >= 0) {
+ int bottom = y + rects[obj].source.y1;
+ if (bottom > s.bottom)
+ break;
+ int right = s.x + rects[obj].source.x1;
+ if (right > w)
+ break;
+ bins[s.size]++;
+ rects[obj].source.x0 = s.x;
+ rects[obj].source.x1 += s.x;
+ rects[obj].source.y0 = y;
+ rects[obj].source.y1 += y;
+ num_rects--;
+ if (maxy <= 0)
+ stack[stackpos++] = s;
+ s.x = right;
+ maxy = FFMAX(maxy, bottom);
+ }
+ if (maxy > 0)
+ s.bottom = maxy;
+ }
+ }
+ return num_rects ? -1 : 0;
}
-static void generate_eosd(mp_eosd_images_t *imgs)
+static void generate_eosd(struct vo *vo, mp_eosd_images_t *imgs)
{
+ struct vdpctx *vc = vo->priv;
+ struct vdp_functions *vdp = vc->vdp;
VdpStatus vdp_st;
- VdpRect destRect;
- int j, found;
- ass_image_t *img = imgs->imgs;
- ass_image_t *i;
+ int i;
+ ASS_Image *img = imgs->imgs;
+ ASS_Image *p;
+ struct eosd_bitmap_surface *sfc = &vc->eosd_surface;
+ bool need_upload = false;
- // Nothing changed, no need to redraw
if (imgs->changed == 0)
- return;
- eosd_render_count = 0;
- // There's nothing to render!
+ return; // Nothing changed, no need to redraw
+
+ vc->eosd_render_count = 0;
+
if (!img)
- return;
+ return; // There's nothing to render!
if (imgs->changed == 1)
goto eosd_skip_upload;
- for (j = 0; j < eosd_surface_count; j++)
- eosd_surfaces[j].in_use = 0;
-
- for (i = img; i; i = i->next) {
- // Try to reuse a suitable surface
- found = -1;
- for (j = 0; j < eosd_surface_count; j++) {
- if (eosd_surfaces[j].surface != VDP_INVALID_HANDLE && !eosd_surfaces[j].in_use &&
- eosd_surfaces[j].w >= i->w && eosd_surfaces[j].h >= i->h) {
- found = j;
- break;
- }
- }
- // None found, allocate a new surface
- if (found < 0) {
- for (j = 0; j < eosd_surface_count; j++) {
- if (!eosd_surfaces[j].in_use) {
- if (eosd_surfaces[j].surface != VDP_INVALID_HANDLE)
- vdp_bitmap_surface_destroy(eosd_surfaces[j].surface);
- found = j;
- break;
- }
- }
+ need_upload = true;
+ bool reallocate = false;
+ while (1) {
+ for (p = img, i = 0; p; p = p->next) {
+ if (p->w <= 0 || p->h <= 0)
+ continue;
// Allocate new space for surface/target arrays
- if (found < 0) {
- j = found = eosd_surface_count;
- eosd_surface_count = eosd_surface_count ? eosd_surface_count*2 : EOSD_SURFACES_INITIAL;
- eosd_surfaces = realloc(eosd_surfaces, eosd_surface_count * sizeof(*eosd_surfaces));
- eosd_targets = realloc(eosd_targets, eosd_surface_count * sizeof(*eosd_targets));
- for (j = found; j < eosd_surface_count; j++) {
- eosd_surfaces[j].surface = VDP_INVALID_HANDLE;
- eosd_surfaces[j].in_use = 0;
- }
+ if (i >= vc->eosd_targets_size) {
+ vc->eosd_targets_size = FFMAX(vc->eosd_targets_size * 2, 512);
+ vc->eosd_targets =
+ talloc_realloc_size(vc, vc->eosd_targets,
+ vc->eosd_targets_size
+ * sizeof(*vc->eosd_targets));
+ vc->eosd_scratch =
+ talloc_realloc_size(vc, vc->eosd_scratch,
+ (vc->eosd_targets_size + 16)
+ * sizeof(*vc->eosd_scratch));
}
- vdp_st = vdp_bitmap_surface_create(vdp_device, VDP_RGBA_FORMAT_A8,
- i->w, i->h, VDP_TRUE, &eosd_surfaces[found].surface);
- CHECK_ST_WARNING("EOSD: error when creating surface")
- eosd_surfaces[found].w = i->w;
- eosd_surfaces[found].h = i->h;
+ vc->eosd_targets[i].source.x1 = p->w;
+ vc->eosd_targets[i].source.y1 = p->h;
+ i++;
}
- eosd_surfaces[found].in_use = 1;
- eosd_targets[eosd_render_count].surface = eosd_surfaces[found].surface;
- destRect.x0 = 0;
- destRect.y0 = 0;
- destRect.x1 = i->w;
- destRect.y1 = i->h;
- vdp_st = vdp_bitmap_surface_putbits_native(eosd_targets[eosd_render_count].surface,
- (const void *) &i->bitmap, &i->stride, &destRect);
- CHECK_ST_WARNING("EOSD: putbits failed")
- eosd_render_count++;
+ if (pack_rectangles(vc->eosd_targets, i, sfc->w, sfc->h,
+ vc->eosd_scratch) >= 0)
+ break;
+ int w = FFMIN(FFMAX(sfc->w * 2, EOSD_SURFACE_INITIAL_SIZE),
+ sfc->max_width);
+ int h = FFMIN(FFMAX(sfc->h * 2, EOSD_SURFACE_INITIAL_SIZE),
+ sfc->max_height);
+ if (w == sfc->w && h == sfc->h) {
+ mp_msg(MSGT_VO, MSGL_ERR, "[vdpau] EOSD bitmaps do not fit on "
+ "a surface with the maximum supported size\n");
+ return;
+ } else {
+ sfc->w = w;
+ sfc->h = h;
+ }
+ reallocate = true;
+ }
+ if (reallocate) {
+ if (sfc->surface != VDP_INVALID_HANDLE)
+ vdp->bitmap_surface_destroy(sfc->surface);
+ mp_msg(MSGT_VO, MSGL_V, "[vdpau] Allocating a %dx%d surface for "
+ "EOSD bitmaps.\n", sfc->w, sfc->h);
+ vdp_st = vdp->bitmap_surface_create(vc->vdp_device, VDP_RGBA_FORMAT_A8,
+ sfc->w, sfc->h, true,
+ &sfc->surface);
+ if (vdp_st != VDP_STATUS_OK)
+ sfc->surface = VDP_INVALID_HANDLE;
+ CHECK_ST_WARNING("EOSD: error when creating surface");
}
eosd_skip_upload:
- eosd_render_count = 0;
- for (i = img; i; i = i->next) {
+ if (sfc->surface == VDP_INVALID_HANDLE)
+ return;
+ for (p = img; p; p = p->next) {
+ if (p->w <= 0 || p->h <= 0)
+ continue;
+ struct eosd_target *target = &vc->eosd_targets[vc->eosd_render_count];
+ if (need_upload) {
+ vdp_st = vdp->
+ bitmap_surface_put_bits_native(sfc->surface,
+ (const void *) &p->bitmap,
+ &p->stride, &target->source);
+ CHECK_ST_WARNING("EOSD: putbits failed");
+ }
// Render dest, color, etc.
- eosd_targets[eosd_render_count].color.alpha = 1.0 - ((i->color >> 0) & 0xff) / 255.0;
- eosd_targets[eosd_render_count].color.blue = ((i->color >> 8) & 0xff) / 255.0;
- eosd_targets[eosd_render_count].color.green = ((i->color >> 16) & 0xff) / 255.0;
- eosd_targets[eosd_render_count].color.red = ((i->color >> 24) & 0xff) / 255.0;
- eosd_targets[eosd_render_count].dest.x0 = i->dst_x;
- eosd_targets[eosd_render_count].dest.y0 = i->dst_y;
- eosd_targets[eosd_render_count].dest.x1 = i->w + i->dst_x;
- eosd_targets[eosd_render_count].dest.y1 = i->h + i->dst_y;
- eosd_targets[eosd_render_count].source.x0 = 0;
- eosd_targets[eosd_render_count].source.y0 = 0;
- eosd_targets[eosd_render_count].source.x1 = i->w;
- eosd_targets[eosd_render_count].source.y1 = i->h;
- eosd_render_count++;
+ target->color.alpha = 1.0 - ((p->color >> 0) & 0xff) / 255.0;
+ target->color.blue = ((p->color >> 8) & 0xff) / 255.0;
+ target->color.green = ((p->color >> 16) & 0xff) / 255.0;
+ target->color.red = ((p->color >> 24) & 0xff) / 255.0;
+ target->dest.x0 = p->dst_x;
+ target->dest.y0 = p->dst_y;
+ target->dest.x1 = p->w + p->dst_x;
+ target->dest.y1 = p->h + p->dst_y;
+ vc->eosd_render_count++;
}
}
-static void draw_osd(void)
+static void draw_osd(struct vo *vo, struct osd_state *osd)
{
+ struct vdpctx *vc = vo->priv;
mp_msg(MSGT_VO, MSGL_DBG2, "DRAW_OSD\n");
- if (handle_preemption() < 0)
+ if (handle_preemption(vo) < 0)
return;
- vo_draw_text_ext(vo_dwidth, vo_dheight, border_x, border_y, border_x, border_y,
- vid_width, vid_height, draw_osd_I8A8);
+ osd_draw_text_ext(osd, vo->dwidth, vo->dheight, vc->border_x, vc->border_y,
+ vc->border_x, vc->border_y, vc->vid_width,
+ vc->vid_height, draw_osd_I8A8, vo);
}
-static void flip_page(void)
+static void wait_for_previous_frame(struct vo *vo)
{
+ struct vdpctx *vc = vo->priv;
+ struct vdp_functions *vdp = vc->vdp;
VdpStatus vdp_st;
- mp_msg(MSGT_VO, MSGL_DBG2, "\nFLIP_PAGE VID:%u -> OUT:%u\n",
- surface_render[vid_surface_num].surface, output_surfaces[surface_num]);
- if (handle_preemption() < 0)
+ if (vc->num_shown_frames < 2)
return;
- vdp_st = vdp_presentation_queue_display(vdp_flip_queue, output_surfaces[surface_num],
- vo_dwidth, vo_dheight,
- 0);
- CHECK_ST_WARNING("Error when calling vdp_presentation_queue_display")
+ VdpTime vtime;
+ VdpOutputSurface visible_s, prev_s;
+ int base = vc->surface_num + NUM_OUTPUT_SURFACES;
+ visible_s = vc->output_surfaces[(base - 1) % NUM_OUTPUT_SURFACES];
+ prev_s = vc->output_surfaces[(base - 2) % NUM_OUTPUT_SURFACES];
+ vdp_st = vdp->presentation_queue_block_until_surface_idle(vc->flip_queue,
+ prev_s, &vtime);
+ CHECK_ST_WARNING("Error calling "
+ "presentation_queue_block_until_surface_idle");
+ VdpPresentationQueueStatus status;
+ vdp_st = vdp->presentation_queue_query_surface_status(vc->flip_queue,
+ visible_s,
+ &status, &vtime);
+ CHECK_ST_WARNING("Error calling presentation_queue_query_surface_status");
+ vc->recent_vsync_time = vtime;
+}
- surface_num = (surface_num + 1) % NUM_OUTPUT_SURFACES;
- visible_buf = 1;
+static inline uint64_t prev_vs2(struct vdpctx *vc, uint64_t ts, int shift)
+{
+ uint64_t offset = ts - vc->recent_vsync_time;
+ // Fix negative values for 1<<shift vsyncs before vc->recent_vsync_time
+ offset += (uint64_t)vc->vsync_interval << shift;
+ offset %= vc->vsync_interval;
+ return ts - offset;
}
-static int draw_slice(uint8_t *image[], int stride[], int w, int h,
- int x, int y)
+static void flip_page_timed(struct vo *vo, unsigned int pts_us, int duration)
{
+ struct vdpctx *vc = vo->priv;
+ struct vdp_functions *vdp = vc->vdp;
VdpStatus vdp_st;
- struct vdpau_render_state *rndr = (struct vdpau_render_state *)image[0];
- int max_refs = image_format == IMGFMT_VDPAU_H264 ? rndr->info.h264.num_ref_frames : 2;
+ uint32_t vsync_interval = vc->vsync_interval;
- if (handle_preemption() < 0)
+ if (handle_preemption(vo) < 0)
+ return;
+
+ if (duration > INT_MAX / 1000)
+ duration = -1;
+ else
+ duration *= 1000;
+
+ if (vc->user_fps < 0)
+ duration = -1; // Make sure drop logic is disabled
+
+ uint64_t now = sync_vdptime(vo);
+ uint64_t pts = pts_us ? convert_to_vdptime(vo, pts_us) : now;
+ uint64_t ideal_pts = pts;
+ uint64_t npts = duration >= 0 ? pts + duration : UINT64_MAX;
+
+#define PREV_VS2(ts, shift) prev_vs2(vc, ts, shift)
+ // Only gives accurate results for ts >= vc->recent_vsync_time
+#define PREV_VSYNC(ts) PREV_VS2(ts, 0)
+
+ /* We hope to be here at least one vsync before the frame should be shown.
+ * If we are running late then don't drop the frame unless there is
+ * already one queued for the next vsync; even if we _hope_ to show the
+ * next frame soon enough to mean this one should be dropped we might
+ * not make the target time in reality. Without this check we could drop
+ * every frame, freezing the display completely if video lags behind.
+ */
+ if (now > PREV_VSYNC(FFMAX(pts,
+ vc->last_queue_time + vsync_interval)))
+ npts = UINT64_MAX;
+
+ /* Allow flipping a frame at a vsync if its presentation time is a
+ * bit after that vsync and the change makes the flip time delta
+ * from previous frame better match the target timestamp delta.
+ * This avoids instability with frame timestamps falling near vsyncs.
+ * For example if the frame timestamps were (with vsyncs at
+ * integer values) 0.01, 1.99, 4.01, 5.99, 8.01, ... then
+ * straightforward timing at next vsync would flip the frames at
+ * 1, 2, 5, 6, 9; this changes it to 1, 2, 4, 6, 8 and so on with
+ * regular 2-vsync intervals.
+ *
+ * Also allow moving the frame forward if it looks like we dropped
+ * the previous frame incorrectly (now that we know better after
+ * having final exact timestamp information for this frame) and
+ * there would unnecessarily be a vsync without a frame change.
+ */
+ uint64_t vsync = PREV_VSYNC(pts);
+ if (pts < vsync + vsync_interval / 4
+ && (vsync - PREV_VS2(vc->last_queue_time, 16)
+ > pts - vc->last_ideal_time + vsync_interval / 2
+ || vc->dropped_frame && vsync > vc->dropped_time))
+ pts -= vsync_interval / 2;
+
+ vc->dropped_frame = true; // changed at end if false
+ vc->dropped_time = ideal_pts;
+
+ pts = FFMAX(pts, vc->last_queue_time + vsync_interval);
+ pts = FFMAX(pts, now);
+ if (npts < PREV_VSYNC(pts) + vsync_interval)
+ return;
+
+ /* At least on my NVIDIA 9500GT with driver versions 185.18.36 and 190.42
+ * trying to queue two unshown frames simultaneously caused bad behavior
+ * (high CPU use in _other_ VDPAU functions called later). Avoiding
+ * longer queues also makes things simpler. So currently we always
+ * try to keep exactly one frame queued for the future, queuing the
+ * current frame immediately after the previous one is shown.
+ */
+ wait_for_previous_frame(vo);
+
+ now = sync_vdptime(vo);
+ pts = FFMAX(pts, now);
+ vsync = PREV_VSYNC(pts);
+ if (npts < vsync + vsync_interval)
+ return;
+ pts = vsync + (vsync_interval >> 2);
+ vdp_st =
+ vdp->presentation_queue_display(vc->flip_queue,
+ vc->output_surfaces[vc->surface_num],
+ vo->dwidth, vo->dheight, pts);
+ CHECK_ST_WARNING("Error when calling vdp_presentation_queue_display");
+
+ vc->last_queue_time = pts;
+ vc->last_ideal_time = ideal_pts;
+ vc->dropped_frame = false;
+ vc->surface_num = (vc->surface_num + 1) % NUM_OUTPUT_SURFACES;
+ vc->num_shown_frames = FFMIN(vc->num_shown_frames + 1, 1000);
+}
+
+static int draw_slice(struct vo *vo, uint8_t *image[], int stride[], int w,
+ int h, int x, int y)
+{
+ struct vdpctx *vc = vo->priv;
+ struct vdp_functions *vdp = vc->vdp;
+ VdpStatus vdp_st;
+
+ if (handle_preemption(vo) < 0)
return VO_TRUE;
- if (!IMGFMT_IS_VDPAU(image_format))
+ struct vdpau_render_state *rndr = (struct vdpau_render_state *)image[0];
+ int max_refs = vc->image_format == IMGFMT_VDPAU_H264 ?
+ rndr->info.h264.num_ref_frames : 2;
+ if (!IMGFMT_IS_VDPAU(vc->image_format))
return VO_FALSE;
- if ((decoder == VDP_INVALID_HANDLE || decoder_max_refs < max_refs)
- && !create_vdp_decoder(image_format, vid_width, vid_height, max_refs))
+ if ((vc->decoder == VDP_INVALID_HANDLE || vc->decoder_max_refs < max_refs)
+ && !create_vdp_decoder(vo, max_refs))
return VO_FALSE;
- vdp_st = vdp_decoder_render(decoder, rndr->surface, (void *)&rndr->info, rndr->bitstream_buffers_used, rndr->bitstream_buffers);
+ vdp_st = vdp->decoder_render(vc->decoder, rndr->surface,
+ (void *)&rndr->info,
+ rndr->bitstream_buffers_used,
+ rndr->bitstream_buffers);
CHECK_ST_WARNING("Failed VDPAU decoder rendering");
return VO_TRUE;
}
-static int draw_frame(uint8_t *src[])
+static struct vdpau_render_state *get_surface(struct vo *vo, int number)
{
- return VO_ERROR;
-}
+ struct vdpctx *vc = vo->priv;
+ struct vdp_functions *vdp = vc->vdp;
-static struct vdpau_render_state *get_surface(int number)
-{
if (number > MAX_VIDEO_SURFACES)
return NULL;
- if (surface_render[number].surface == VDP_INVALID_HANDLE) {
+ if (vc->surface_render[number].surface == VDP_INVALID_HANDLE
+ && !vc->is_preempted) {
VdpStatus vdp_st;
- vdp_st = vdp_video_surface_create(vdp_device, vdp_chroma_type,
- vid_width, vid_height,
- &surface_render[number].surface);
- CHECK_ST_WARNING("Error when calling vdp_video_surface_create")
- if (vdp_st != VDP_STATUS_OK)
- return NULL;
+ vdp_st = vdp->video_surface_create(vc->vdp_device, vc->vdp_chroma_type,
+ vc->vid_width, vc->vid_height,
+ &vc->surface_render[number].surface);
+ CHECK_ST_WARNING("Error when calling vdp_video_surface_create");
}
- mp_msg(MSGT_VO, MSGL_DBG2, "VID CREATE: %u\n", surface_render[number].surface);
- return &surface_render[number];
+ mp_msg(MSGT_VO, MSGL_DBG2, "VID CREATE: %u\n",
+ vc->surface_render[number].surface);
+ return &vc->surface_render[number];
}
-static uint32_t draw_image(mp_image_t *mpi)
+static void draw_image(struct vo *vo, mp_image_t *mpi, double pts)
{
- if (IMGFMT_IS_VDPAU(image_format)) {
- struct vdpau_render_state *rndr = mpi->priv;
- vid_surface_num = rndr - surface_render;
- if (deint_buffer_past_frames) {
- mpi->usage_count++;
- if (deint_mpi[1])
- deint_mpi[1]->usage_count--;
- deint_mpi[1] = deint_mpi[0];
- deint_mpi[0] = mpi;
- }
- } else if (image_format == IMGFMT_BGRA) {
- VdpStatus vdp_st;
- VdpRect r = {0, 0, vid_width, vid_height};
- vdp_st = vdp_output_surface_put_bits_native(output_surfaces[2],
- (void const*const*)mpi->planes,
- mpi->stride, &r);
- CHECK_ST_ERROR("Error when calling vdp_output_surface_put_bits_native")
- vdp_st = vdp_output_surface_render_output_surface(output_surfaces[surface_num],
- &out_rect_vid,
- output_surfaces[2],
- &src_rect_vid, NULL, NULL,
- VDP_OUTPUT_SURFACE_RENDER_ROTATE_0);
- CHECK_ST_ERROR("Error when calling vdp_output_surface_render_output_surface")
+ struct vdpctx *vc = vo->priv;
+ struct vdp_functions *vdp = vc->vdp;
+ struct mp_image *reserved_mpi = NULL;
+ struct vdpau_render_state *rndr;
+
+ if (vc->is_preempted) {
+ vo->frame_loaded = true;
+ return;
+ }
+
+ if (IMGFMT_IS_VDPAU(vc->image_format)) {
+ rndr = mpi->priv;
+ reserved_mpi = mpi;
} else if (!(mpi->flags & MP_IMGFLAG_DRAW_CALLBACK)) {
VdpStatus vdp_st;
void *destdata[3] = {mpi->planes[0], mpi->planes[2], mpi->planes[1]};
- struct vdpau_render_state *rndr = get_surface(deint_counter);
- deint_counter = (deint_counter + 1) % 3;
- vid_surface_num = rndr - surface_render;
- if (image_format == IMGFMT_NV12)
+ rndr = get_surface(vo, vc->deint_counter);
+ vc->deint_counter = (vc->deint_counter + 1) % NUM_BUFFERED_VIDEO;
+ if (vc->image_format == IMGFMT_NV12)
destdata[1] = destdata[2];
- vdp_st = vdp_video_surface_put_bits_y_cb_cr(rndr->surface,
- vdp_pixel_format,
- (const void *const*)destdata,
- mpi->stride); // pitch
- CHECK_ST_ERROR("Error when calling vdp_video_surface_put_bits_y_cb_cr")
- }
+ vdp_st =
+ vdp->video_surface_put_bits_y_cb_cr(rndr->surface,
+ vc->vdp_pixel_format,
+ (const void *const*)destdata,
+ mpi->stride); // pitch
+ CHECK_ST_WARNING("Error when calling "
+ "vdp_video_surface_put_bits_y_cb_cr");
+ } else
+ // We don't support slice callbacks so this shouldn't occur -
+ // I think the flags test above in pointless, but I'm adding
+ // this instead of removing it just in case.
+ abort();
if (mpi->fields & MP_IMGFIELD_ORDERED)
- top_field_first = !!(mpi->fields & MP_IMGFIELD_TOP_FIRST);
+ vc->top_field_first = !!(mpi->fields & MP_IMGFIELD_TOP_FIRST);
else
- top_field_first = 1;
+ vc->top_field_first = 1;
- video_to_output_surface();
- return VO_TRUE;
+ add_new_video_surface(vo, rndr->surface, mpi, pts);
+
+ return;
}
-static uint32_t get_image(mp_image_t *mpi)
+static uint32_t get_image(struct vo *vo, mp_image_t *mpi)
{
+ struct vdpctx *vc = vo->priv;
struct vdpau_render_state *rndr;
// no dr for non-decoding for now
- if (!IMGFMT_IS_VDPAU(image_format))
+ if (!IMGFMT_IS_VDPAU(vc->image_format))
return VO_FALSE;
if (mpi->type != MP_IMGTYPE_NUMBERED)
return VO_FALSE;
- rndr = get_surface(mpi->number);
+ rndr = get_surface(vo, mpi->number);
if (!rndr) {
- mp_msg(MSGT_VO, MSGL_ERR, "[vdpau] no surfaces available in get_image\n");
+ mp_msg(MSGT_VO, MSGL_ERR, "[vdpau] no surfaces available in "
+ "get_image\n");
// TODO: this probably breaks things forever, provide a dummy buffer?
return VO_FALSE;
}
@@ -1095,332 +1497,326 @@ static uint32_t get_image(mp_image_t *mpi)
static int query_format(uint32_t format)
{
- int default_flags = VFCAP_CSP_SUPPORTED | VFCAP_CSP_SUPPORTED_BY_HW | VFCAP_HWSCALE_UP | VFCAP_HWSCALE_DOWN | VFCAP_OSD | VFCAP_EOSD | VFCAP_EOSD_UNSCALED | VFCAP_FLIP;
+ int default_flags = VFCAP_CSP_SUPPORTED | VFCAP_CSP_SUPPORTED_BY_HW
+ | VFCAP_HWSCALE_UP | VFCAP_HWSCALE_DOWN | VFCAP_OSD | VFCAP_EOSD
+ | VFCAP_EOSD_UNSCALED | VFCAP_FLIP;
switch (format) {
- case IMGFMT_BGRA:
- if (force_mixer)
- return 0;
- case IMGFMT_YV12:
- case IMGFMT_I420:
- case IMGFMT_IYUV:
- case IMGFMT_NV12:
- case IMGFMT_YUY2:
- case IMGFMT_UYVY:
- return default_flags | VOCAP_NOSLICES;
- case IMGFMT_VDPAU_MPEG1:
- case IMGFMT_VDPAU_MPEG2:
- case IMGFMT_VDPAU_H264:
- case IMGFMT_VDPAU_WMV3:
- case IMGFMT_VDPAU_VC1:
- case IMGFMT_VDPAU_MPEG4:
- if (create_vdp_decoder(format, 48, 48, 2))
+ case IMGFMT_YV12:
+ case IMGFMT_I420:
+ case IMGFMT_IYUV:
+ case IMGFMT_NV12:
+ case IMGFMT_YUY2:
+ case IMGFMT_UYVY:
+ return default_flags | VOCAP_NOSLICES;
+ case IMGFMT_VDPAU_MPEG1:
+ case IMGFMT_VDPAU_MPEG2:
+ case IMGFMT_VDPAU_H264:
+ case IMGFMT_VDPAU_WMV3:
+ case IMGFMT_VDPAU_VC1:
+ case IMGFMT_VDPAU_MPEG4:
return default_flags;
}
return 0;
}
-static void DestroyVdpauObjects(void)
+static void destroy_vdpau_objects(struct vo *vo)
{
+ struct vdpctx *vc = vo->priv;
+ struct vdp_functions *vdp = vc->vdp;
+
int i;
VdpStatus vdp_st;
- free_video_specific();
+ free_video_specific(vo);
- vdp_st = vdp_presentation_queue_destroy(vdp_flip_queue);
- CHECK_ST_WARNING("Error when calling vdp_presentation_queue_destroy")
+ if (vc->flip_queue != VDP_INVALID_HANDLE) {
+ vdp_st = vdp->presentation_queue_destroy(vc->flip_queue);
+ CHECK_ST_WARNING("Error when calling vdp_presentation_queue_destroy");
+ }
- vdp_st = vdp_presentation_queue_target_destroy(vdp_flip_target);
- CHECK_ST_WARNING("Error when calling vdp_presentation_queue_target_destroy")
+ if (vc->flip_target != VDP_INVALID_HANDLE) {
+ vdp_st = vdp->presentation_queue_target_destroy(vc->flip_target);
+ CHECK_ST_WARNING("Error when calling "
+ "vdp_presentation_queue_target_destroy");
+ }
for (i = 0; i <= NUM_OUTPUT_SURFACES; i++) {
- vdp_st = vdp_output_surface_destroy(output_surfaces[i]);
- output_surfaces[i] = VDP_INVALID_HANDLE;
- CHECK_ST_WARNING("Error when calling vdp_output_surface_destroy")
+ if (vc->output_surfaces[i] == VDP_INVALID_HANDLE)
+ continue;
+ vdp_st = vdp->output_surface_destroy(vc->output_surfaces[i]);
+ CHECK_ST_WARNING("Error when calling vdp_output_surface_destroy");
}
- for (i = 0; i<eosd_surface_count; i++) {
- if (eosd_surfaces[i].surface != VDP_INVALID_HANDLE) {
- vdp_st = vdp_bitmap_surface_destroy(eosd_surfaces[i].surface);
- CHECK_ST_WARNING("Error when calling vdp_bitmap_surface_destroy")
- }
- eosd_surfaces[i].surface = VDP_INVALID_HANDLE;
+ if (vc->eosd_surface.surface != VDP_INVALID_HANDLE) {
+ vdp_st = vdp->bitmap_surface_destroy(vc->eosd_surface.surface);
+ CHECK_ST_WARNING("Error when calling vdp_bitmap_surface_destroy");
}
- vdp_st = vdp_device_destroy(vdp_device);
- CHECK_ST_WARNING("Error when calling vdp_device_destroy")
+ vdp_st = vdp->device_destroy(vc->vdp_device);
+ CHECK_ST_WARNING("Error when calling vdp_device_destroy");
}
-static void uninit(void)
+static void uninit(struct vo *vo)
{
- int i;
-
- if (!vo_config_count)
- return;
- visible_buf = 0;
-
- for (i = 0; i < MAX_VIDEO_SURFACES; i++) {
- // Allocated in ff_vdpau_add_data_chunk()
- av_freep(&surface_render[i].bitstream_buffers);
- surface_render[i].bitstream_buffers_allocated = 0;
- }
+ struct vdpctx *vc = vo->priv;
/* Destroy all vdpau objects */
- DestroyVdpauObjects();
-
- free(index_data);
- index_data = NULL;
-
- free(eosd_surfaces);
- eosd_surfaces = NULL;
- free(eosd_targets);
- eosd_targets = NULL;
+ destroy_vdpau_objects(vo);
#ifdef CONFIG_XF86VM
- vo_vm_close();
+ if (vc->mode_switched)
+ vo_vm_close(vo);
#endif
- vo_x11_uninit();
-}
+ vo_x11_uninit(vo);
-static const opt_t subopts[] = {
- {"deint", OPT_ARG_INT, &deint, (opt_test_f)int_non_neg},
- {"chroma-deint", OPT_ARG_BOOL, &chroma_deint, NULL},
- {"pullup", OPT_ARG_BOOL, &pullup, NULL},
- {"denoise", OPT_ARG_FLOAT, &denoise, NULL},
- {"sharpen", OPT_ARG_FLOAT, &sharpen, NULL},
- {"colorspace", OPT_ARG_INT, &colorspace, NULL},
- {"force-mixer", OPT_ARG_BOOL, &force_mixer, NULL},
- {"hqscaling", OPT_ARG_INT, &hqscaling, (opt_test_f)int_non_neg},
- {NULL}
-};
+ // Free bitstream buffers allocated by FFmpeg
+ for (int i = 0; i < MAX_VIDEO_SURFACES; i++)
+ av_freep(&vc->surface_render[i].bitstream_buffers);
+
+ dlclose(vc->vdpau_lib_handle);
+}
-static const char help_msg[] =
- "\n-vo vdpau command line help:\n"
- "Example: mplayer -vo vdpau:deint=2\n"
- "\nOptions:\n"
- " deint (all modes > 0 respect -field-dominance)\n"
- " 0: no deinterlacing\n"
- " 1: only show first field\n"
- " 2: bob deinterlacing\n"
- " 3: temporal deinterlacing (resource-hungry)\n"
- " 4: temporal-spatial deinterlacing (very resource-hungry)\n"
- " chroma-deint\n"
- " Operate on luma and chroma when using temporal deinterlacing (default)\n"
- " Use nochroma-deint to speed up temporal deinterlacing\n"
- " pullup\n"
- " Try to apply inverse-telecine (needs temporal deinterlacing)\n"
- " denoise\n"
- " Apply denoising, argument is strength from 0.0 to 1.0\n"
- " sharpen\n"
- " Apply sharpening or softening, argument is strength from -1.0 to 1.0\n"
- " colorspace\n"
- " 0: guess based on video resolution\n"
- " 1: ITU-R BT.601 (default)\n"
- " 2: ITU-R BT.709\n"
- " 3: SMPTE-240M\n"
- " hqscaling\n"
- " 0: default VDPAU scaler\n"
- " 1-9: high quality VDPAU scaler (needs capable hardware)\n"
- " force-mixer\n"
- " Use the VDPAU mixer (default)\n"
- " Use noforce-mixer to allow BGRA output (disables all above options)\n"
- ;
-
-static int preinit(const char *arg)
+static int preinit(struct vo *vo, const char *arg)
{
int i;
- deint = 0;
- deint_type = 3;
- deint_counter = 0;
- deint_buffer_past_frames = 0;
- deint_mpi[0] = deint_mpi[1] = NULL;
- chroma_deint = 1;
- pullup = 0;
- denoise = 0;
- sharpen = 0;
- colorspace = 1;
- force_mixer = 1;
- hqscaling = 0;
+ struct vdpctx *vc = talloc_zero(vo, struct vdpctx);
+ vo->priv = vc;
+
+ // Mark everything as invalid first so uninit() can tell what has been
+ // allocated
+ mark_vdpau_objects_uninitialized(vo);
+
+ vc->deint_type = 3;
+ vc->chroma_deint = 1;
+ vc->user_colorspace = 1;
+ const opt_t subopts[] = {
+ {"deint", OPT_ARG_INT, &vc->deint, (opt_test_f)int_non_neg},
+ {"chroma-deint", OPT_ARG_BOOL, &vc->chroma_deint, NULL},
+ {"pullup", OPT_ARG_BOOL, &vc->pullup, NULL},
+ {"denoise", OPT_ARG_FLOAT, &vc->denoise, NULL},
+ {"sharpen", OPT_ARG_FLOAT, &vc->sharpen, NULL},
+ {"colorspace", OPT_ARG_INT, &vc->user_colorspace, NULL},
+ {"hqscaling", OPT_ARG_INT, &vc->hqscaling, NULL},
+ {"fps", OPT_ARG_FLOAT, &vc->user_fps, NULL},
+ {NULL}
+ };
if (subopt_parse(arg, subopts) != 0) {
- mp_msg(MSGT_VO, MSGL_FATAL, help_msg);
+ mp_msg(MSGT_VO, MSGL_FATAL, "[vdpau] Could not parse suboptions.\n");
return -1;
}
- if (deint)
- deint_type = deint;
- if (deint > 1)
- deint_buffer_past_frames = 1;
- if (colorspace < 0 || colorspace > 3) {
- mp_msg(MSGT_VO, MSGL_WARN, "[vdpau] Invalid color space specified, "
- "using BT.601\n");
- colorspace = 1;
+ if (vc->hqscaling < 0 || vc->hqscaling > 9) {
+ mp_msg(MSGT_VO, MSGL_FATAL, "[vdpau] Invalid value for suboption "
+ "hqscaling\n");
+ return -1;
}
-
- if (!vo_init() || win_x11_init_vdpau_procs())
+ if (vc->deint)
+ vc->deint_type = vc->deint;
+
+ char *vdpaulibrary = "libvdpau.so.1";
+ char *vdpau_device_create = "vdp_device_create_x11";
+ vc->vdpau_lib_handle = dlopen(vdpaulibrary, RTLD_LAZY);
+ if (!vc->vdpau_lib_handle) {
+ mp_msg(MSGT_VO, MSGL_ERR,
+ "[vdpau] Could not open dynamic library %s\n", vdpaulibrary);
return -1;
+ }
+ vc->vdp_device_create = dlsym(vc->vdpau_lib_handle, vdpau_device_create);
+ if (!vc->vdp_device_create) {
+ mp_msg(MSGT_VO, MSGL_ERR, "[vdpau] Could not find function %s in %s\n",
+ vdpau_device_create, vdpaulibrary);
+ dlclose(vc->vdpau_lib_handle);
+ return -1;
+ }
+ if (!vo_init(vo)) {
+ dlclose(vc->vdpau_lib_handle);
+ return -1;
+ }
- decoder = VDP_INVALID_HANDLE;
- for (i = 0; i < MAX_VIDEO_SURFACES; i++)
- surface_render[i].surface = VDP_INVALID_HANDLE;
- video_mixer = VDP_INVALID_HANDLE;
- for (i = 0; i <= NUM_OUTPUT_SURFACES; i++)
- output_surfaces[i] = VDP_INVALID_HANDLE;
- vdp_flip_queue = VDP_INVALID_HANDLE;
- output_surface_width = output_surface_height = -1;
+ // After this calling uninit() should work to free resources
+
+ if (win_x11_init_vdpau_procs(vo) < 0) {
+ if (vc->vdp->device_destroy)
+ vc->vdp->device_destroy(vc->vdp_device);
+ vo_x11_uninit(vo);
+ dlclose(vc->vdpau_lib_handle);
+ return -1;
+ }
// full grayscale palette.
for (i = 0; i < PALETTE_SIZE; ++i)
- palette[i] = (i << 16) | (i << 8) | i;
- index_data = NULL;
- index_data_size = 0;
-
- eosd_surface_count = eosd_render_count = 0;
- eosd_surfaces = NULL;
- eosd_targets = NULL;
+ vc->palette[i] = (i << 16) | (i << 8) | i;
- procamp.struct_version = VDP_PROCAMP_VERSION;
- procamp.brightness = 0.0;
- procamp.contrast = 1.0;
- procamp.saturation = 1.0;
- procamp.hue = 0.0;
+ vc->procamp.struct_version = VDP_PROCAMP_VERSION;
+ vc->procamp.brightness = 0.0;
+ vc->procamp.contrast = 1.0;
+ vc->procamp.saturation = 1.0;
+ vc->procamp.hue = 0.0;
return 0;
}
-static int get_equalizer(char *name, int *value)
+static int get_equalizer(struct vo *vo, const char *name, int *value)
{
+ struct vdpctx *vc = vo->priv;
+
if (!strcasecmp(name, "brightness"))
- *value = procamp.brightness * 100;
+ *value = vc->procamp.brightness * 100;
else if (!strcasecmp(name, "contrast"))
- *value = (procamp.contrast-1.0) * 100;
+ *value = (vc->procamp.contrast - 1.0) * 100;
else if (!strcasecmp(name, "saturation"))
- *value = (procamp.saturation-1.0) * 100;
+ *value = (vc->procamp.saturation - 1.0) * 100;
else if (!strcasecmp(name, "hue"))
- *value = procamp.hue * 100 / M_PI;
+ *value = vc->procamp.hue * 100 / M_PI;
else
return VO_NOTIMPL;
return VO_TRUE;
}
-static int set_equalizer(char *name, int value)
+static int set_equalizer(struct vo *vo, const char *name, int value)
{
+ struct vdpctx *vc = vo->priv;
+ struct vdp_functions *vdp = vc->vdp;
+
if (!strcasecmp(name, "brightness"))
- procamp.brightness = value / 100.0;
+ vc->procamp.brightness = value / 100.0;
else if (!strcasecmp(name, "contrast"))
- procamp.contrast = value / 100.0 + 1.0;
+ vc->procamp.contrast = value / 100.0 + 1.0;
else if (!strcasecmp(name, "saturation"))
- procamp.saturation = value / 100.0 + 1.0;
+ vc->procamp.saturation = value / 100.0 + 1.0;
else if (!strcasecmp(name, "hue"))
- procamp.hue = value / 100.0 * M_PI;
+ vc->procamp.hue = value / 100.0 * M_PI;
else
return VO_NOTIMPL;
- return update_csc_matrix();
+ update_csc_matrix(vo);
+ return true;
}
-static int control(uint32_t request, void *data, ...)
+static int control(struct vo *vo, uint32_t request, void *data)
{
- if (handle_preemption() < 0)
- return VO_FALSE;
+ struct vdpctx *vc = vo->priv;
+ struct vdp_functions *vdp = vc->vdp;
+
+ handle_preemption(vo);
switch (request) {
case VOCTRL_GET_DEINTERLACE:
- *(int*)data = deint;
+ *(int*)data = vc->deint;
return VO_TRUE;
case VOCTRL_SET_DEINTERLACE:
- if (image_format == IMGFMT_BGRA)
- return VO_NOTIMPL;
- deint = *(int*)data;
- if (deint)
- deint = deint_type;
- if (deint_type > 2) {
+ vc->deint = *(int*)data;
+ if (vc->deint)
+ vc->deint = vc->deint_type;
+ if (vc->deint_type > 2) {
VdpStatus vdp_st;
VdpVideoMixerFeature features[1] =
- {deint_type == 3 ?
+ {vc->deint_type == 3 ?
VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL :
VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL_SPATIAL};
- VdpBool feature_enables[1] = {deint ? VDP_TRUE : VDP_FALSE};
- vdp_st = vdp_video_mixer_set_feature_enables(video_mixer, 1,
- features,
- feature_enables);
- CHECK_ST_WARNING("Error changing deinterlacing settings")
- deint_buffer_past_frames = 1;
+ VdpBool feature_enables[1] = {vc->deint ? VDP_TRUE : VDP_FALSE};
+ vdp_st = vdp->video_mixer_set_feature_enables(vc->video_mixer,
+ 1, features,
+ feature_enables);
+ CHECK_ST_WARNING("Error changing deinterlacing settings");
}
return VO_TRUE;
case VOCTRL_PAUSE:
- return int_pause = 1;
+ if (vc->dropped_frame)
+ flip_page_timed(vo, 0, -1);
+ return (vc->paused = true);
case VOCTRL_RESUME:
- return int_pause = 0;
+ return (vc->paused = false);
case VOCTRL_QUERY_FORMAT:
return query_format(*(uint32_t *)data);
case VOCTRL_GET_IMAGE:
- return get_image(data);
+ return get_image(vo, data);
case VOCTRL_DRAW_IMAGE:
- return draw_image(data);
- case VOCTRL_GUISUPPORT:
- return VO_TRUE;
+ abort(); // draw_image() should get called directly
case VOCTRL_BORDER:
- vo_x11_border();
- resize();
+ vo_x11_border(vo);
+ resize(vo);
return VO_TRUE;
case VOCTRL_FULLSCREEN:
- vo_x11_fullscreen();
- resize();
+ vo_x11_fullscreen(vo);
+ resize(vo);
return VO_TRUE;
case VOCTRL_GET_PANSCAN:
return VO_TRUE;
case VOCTRL_SET_PANSCAN:
- resize();
+ resize(vo);
return VO_TRUE;
case VOCTRL_SET_EQUALIZER: {
- va_list ap;
- int value;
- if (image_format == IMGFMT_BGRA)
- return VO_NOTIMPL;
-
- va_start(ap, data);
- value = va_arg(ap, int);
-
- va_end(ap);
- return set_equalizer(data, value);
+ struct voctrl_set_equalizer_args *args = data;
+ return set_equalizer(vo, args->name, args->value);
}
case VOCTRL_GET_EQUALIZER: {
- va_list ap;
- int *value;
-
- va_start(ap, data);
- value = va_arg(ap, int *);
-
- va_end(ap);
- return get_equalizer(data, value);
+ struct voctrl_get_equalizer_args *args = data;
+ return get_equalizer(vo, args->name, args->valueptr);
}
+ case VOCTRL_SET_YUV_COLORSPACE:
+ vc->colorspace = *(int *)data % 3;
+ update_csc_matrix(vo);
+ return true;
+ case VOCTRL_GET_YUV_COLORSPACE:
+ *(int *)data = vc->colorspace;
+ return true;
case VOCTRL_ONTOP:
- vo_x11_ontop();
+ vo_x11_ontop(vo);
return VO_TRUE;
case VOCTRL_UPDATE_SCREENINFO:
- update_xinerama_info();
+ update_xinerama_info(vo);
return VO_TRUE;
case VOCTRL_DRAW_EOSD:
if (!data)
return VO_FALSE;
- generate_eosd(data);
- draw_eosd();
+ generate_eosd(vo, data);
+ draw_eosd(vo);
return VO_TRUE;
case VOCTRL_GET_EOSD_RES: {
mp_eosd_res_t *r = data;
r->mt = r->mb = r->ml = r->mr = 0;
if (vo_fs) {
- r->w = vo_screenwidth;
- r->h = vo_screenheight;
- r->ml = r->mr = border_x;
- r->mt = r->mb = border_y;
+ r->w = vo->opts->vo_screenwidth;
+ r->h = vo->opts->vo_screenheight;
+ r->ml = r->mr = vc->border_x;
+ r->mt = r->mb = vc->border_y;
} else {
- r->w = vo_dwidth;
- r->h = vo_dheight;
+ r->w = vo->dwidth;
+ r->h = vo->dheight;
}
return VO_TRUE;
}
+ case VOCTRL_REDRAW_OSD:
+ video_to_output_surface(vo);
+ draw_eosd(vo);
+ draw_osd(vo, data);
+ flip_page_timed(vo, 0, -1);
+ return true;
+ case VOCTRL_RESET:
+ forget_frames(vo);
+ return true;
}
return VO_NOTIMPL;
}
-/* @} */
+const struct vo_driver video_out_vdpau = {
+ .is_new = true,
+ .buffer_frames = true,
+ .info = &(const struct vo_info_s){
+ "VDPAU with X11",
+ "vdpau",
+ "Rajib Mahapatra <rmahapatra@nvidia.com> and others",
+ ""
+ },
+ .preinit = preinit,
+ .config = config,
+ .control = control,
+ .draw_image = draw_image,
+ .get_buffered_frame = get_buffered_frame,
+ .draw_slice = draw_slice,
+ .draw_osd = draw_osd,
+ .flip_page_timed = flip_page_timed,
+ .check_events = check_events,
+ .uninit = uninit,
+};