summaryrefslogtreecommitdiffstats
path: root/libvo
diff options
context:
space:
mode:
authorUoti Urpala <uau@glyph.nonexistent.invalid>2009-11-16 04:18:13 +0200
committerUoti Urpala <uau@glyph.nonexistent.invalid>2009-11-16 04:18:13 +0200
commit507f4fe6c7811558b1367e4b64855ae7f9bc8da8 (patch)
tree416b70358bf4b13871cda60fd8e8f64c5e9a2392 /libvo
parent8bbb79c03a594e452fb9109dd6fda781d390a798 (diff)
parentb8861ce8e28080b151467827f65bdc3707a74511 (diff)
downloadmpv-507f4fe6c7811558b1367e4b64855ae7f9bc8da8.tar.bz2
mpv-507f4fe6c7811558b1367e4b64855ae7f9bc8da8.tar.xz
Merge branch 'vdpau'
Diffstat (limited to 'libvo')
-rw-r--r--libvo/vdpau_template.c3
-rw-r--r--libvo/video_out.c9
-rw-r--r--libvo/video_out.h9
-rw-r--r--libvo/vo_vdpau.c492
-rw-r--r--libvo/vo_xv.c11
-rw-r--r--libvo/x11_common.c18
-rw-r--r--libvo/x11_common.h1
7 files changed, 430 insertions, 113 deletions
diff --git a/libvo/vdpau_template.c b/libvo/vdpau_template.c
index ca1a6f6056..3f9b26c5d8 100644
--- a/libvo/vdpau_template.c
+++ b/libvo/vdpau_template.c
@@ -27,10 +27,13 @@ VDP_FUNCTION(VdpPresentationQueueBlockUntilSurfaceIdle, VDP_FUNC_ID_PRESENTATION
VDP_FUNCTION(VdpPresentationQueueCreate, VDP_FUNC_ID_PRESENTATION_QUEUE_CREATE, presentation_queue_create)
VDP_FUNCTION(VdpPresentationQueueDestroy, VDP_FUNC_ID_PRESENTATION_QUEUE_DESTROY, presentation_queue_destroy)
VDP_FUNCTION(VdpPresentationQueueDisplay, VDP_FUNC_ID_PRESENTATION_QUEUE_DISPLAY, presentation_queue_display)
+VDP_FUNCTION(VdpPresentationQueueGetTime, VDP_FUNC_ID_PRESENTATION_QUEUE_GET_TIME, presentation_queue_get_time)
+VDP_FUNCTION(VdpPresentationQueueQuerySurfaceStatus, VDP_FUNC_ID_PRESENTATION_QUEUE_QUERY_SURFACE_STATUS, presentation_queue_query_surface_status)
VDP_FUNCTION(VdpPresentationQueueTargetCreateX11, VDP_FUNC_ID_PRESENTATION_QUEUE_TARGET_CREATE_X11, presentation_queue_target_create_x11)
VDP_FUNCTION(VdpPresentationQueueTargetDestroy, VDP_FUNC_ID_PRESENTATION_QUEUE_TARGET_DESTROY, presentation_queue_target_destroy)
VDP_FUNCTION(VdpVideoMixerCreate, VDP_FUNC_ID_VIDEO_MIXER_CREATE, video_mixer_create)
VDP_FUNCTION(VdpVideoMixerDestroy, VDP_FUNC_ID_VIDEO_MIXER_DESTROY, video_mixer_destroy)
+VDP_FUNCTION(VdpVideoMixerQueryFeatureSupport, VDP_FUNC_ID_VIDEO_MIXER_QUERY_FEATURE_SUPPORT, video_mixer_query_feature_support)
VDP_FUNCTION(VdpVideoMixerRender, VDP_FUNC_ID_VIDEO_MIXER_RENDER, video_mixer_render)
VDP_FUNCTION(VdpVideoMixerSetAttributeValues, VDP_FUNC_ID_VIDEO_MIXER_SET_ATTRIBUTE_VALUES, video_mixer_set_attribute_values)
VDP_FUNCTION(VdpVideoMixerSetFeatureEnables, VDP_FUNC_ID_VIDEO_MIXER_SET_FEATURE_ENABLES, video_mixer_set_feature_enables)
diff --git a/libvo/video_out.c b/libvo/video_out.c
index 7a41fcf1d4..eb3d0183d6 100644
--- a/libvo/video_out.c
+++ b/libvo/video_out.c
@@ -328,13 +328,16 @@ void vo_draw_osd(struct vo *vo, struct osd_state *osd)
vo->driver->draw_osd(vo, osd);
}
-void vo_flip_page(struct vo *vo)
+void vo_flip_page(struct vo *vo, unsigned int pts_us, int duration)
{
if (!vo->config_ok)
return;
vo->frame_loaded = false;
- vo->next_pts = (-1LL<<63); // MP_NOPTS_VALUE
- vo->driver->flip_page(vo);
+ vo->next_pts = MP_NOPTS_VALUE;
+ if (vo->driver->flip_page_timed)
+ vo->driver->flip_page_timed(vo, pts_us, duration);
+ else
+ vo->driver->flip_page(vo);
}
void vo_check_events(struct vo *vo)
diff --git a/libvo/video_out.h b/libvo/video_out.h
index 9bbfd3dc41..17c387a320 100644
--- a/libvo/video_out.h
+++ b/libvo/video_out.h
@@ -30,6 +30,8 @@
#include "libmpcodecs/img_format.h"
//#include "vidix/vidix.h"
+#define MP_NOPTS_VALUE (-1LL<<63)
+
#define VO_EVENT_EXPOSE 1
#define VO_EVENT_RESIZE 2
#define VO_EVENT_KEYPRESS 4
@@ -86,6 +88,9 @@ typedef struct {
#define VOCTRL_UPDATE_SCREENINFO 32
+#define VOCTRL_SET_YUV_COLORSPACE 33
+#define VOCTRL_GET_YUV_COLORSPACE 34
+
// Vo can be used by xover
#define VOCTRL_XOVERLAY_SUPPORT 22
@@ -198,6 +203,7 @@ struct vo_driver {
* Blit/Flip buffer to the screen. Must be called after each frame!
*/
void (*flip_page)(struct vo *vo);
+ void (*flip_page_timed)(struct vo *vo, unsigned int pts_us, int duration);
/*
* This func is called after every frames to handle keyboard and
@@ -231,6 +237,7 @@ struct vo {
bool frame_loaded; // Is there a next frame the VO could flip to?
double next_pts; // pts value of the next frame if any
+ double next_pts2; // optional pts of frame after that
const struct vo_driver *driver;
void *priv;
@@ -274,7 +281,7 @@ int vo_get_buffered_frame(struct vo *vo, bool eof);
int vo_draw_frame(struct vo *vo, uint8_t *src[]);
int vo_draw_slice(struct vo *vo, uint8_t *src[], int stride[], int w, int h, int x, int y);
void vo_draw_osd(struct vo *vo, struct osd_state *osd);
-void vo_flip_page(struct vo *vo);
+void vo_flip_page(struct vo *vo, unsigned int pts_us, int duration);
void vo_check_events(struct vo *vo);
void vo_seek_reset(struct vo *vo);
void vo_destroy(struct vo *vo);
diff --git a/libvo/vo_vdpau.c b/libvo/vo_vdpau.c
index e613205834..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.
*
@@ -32,6 +33,7 @@
#include <dlfcn.h>
#include <stdint.h>
#include <stdbool.h>
+#include <limits.h>
#include "config.h"
#include "mp_msg.h"
@@ -72,8 +74,9 @@
} 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
@@ -104,27 +107,35 @@ struct vdpctx {
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];
- VdpVideoSurface deint_surfaces[3];
- double deint_pts[3];
+ struct buffered_video_surface {
+ VdpVideoSurface surface;
+ double pts;
+ mp_image_t *mpi;
+ } buffered_video[NUM_BUFFERED_VIDEO];
int deint_queue_pos;
- mp_image_t *deint_mpi[3];
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;
@@ -135,6 +146,13 @@ struct vdpctx {
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;
@@ -169,15 +187,57 @@ struct vdpctx {
// Video equalizer
VdpProcamp procamp;
- bool visible_buf;
+ int num_shown_frames;
bool paused;
// These tell what's been initialized and uninit() should free/uninitialize
bool mode_switched;
};
+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 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 uint64_t convert_to_vdptime(struct vo *vo, unsigned int t)
+{
+ struct vdpctx *vc = vo->priv;
+ return (int)(t - vc->last_sync_update) * 1000LL + vc->last_vdp_time;
+}
+
+static void flip_page_timed(struct vo *vo, unsigned int pts_us, int duration);
-static void flip_page(struct vo *vo);
static int video_to_output_surface(struct vo *vo)
{
struct vdpctx *vc = vo->priv;
@@ -187,6 +247,7 @@ static int video_to_output_surface(struct vo *vo)
if (vc->deint_queue_pos < 0)
return -1;
+ struct buffered_video_surface *bv = vc->buffered_video;
int field = VDP_VIDEO_MIXER_PICTURE_STRUCTURE_FRAME;
unsigned int dp = vc->deint_queue_pos;
// dp==0 means last field of latest frame, 1 earlier field of latest frame,
@@ -196,11 +257,10 @@ static int video_to_output_surface(struct vo *vo)
VDP_VIDEO_MIXER_PICTURE_STRUCTURE_BOTTOM_FIELD:
VDP_VIDEO_MIXER_PICTURE_STRUCTURE_TOP_FIELD;
}
- VdpVideoSurface *q = vc->deint_surfaces;
const VdpVideoSurface *past_fields = (const VdpVideoSurface []){
- q[(dp+1)/2], q[(dp+2)/2]};
+ bv[(dp+1)/2].surface, bv[(dp+2)/2].surface};
const VdpVideoSurface *future_fields = (const VdpVideoSurface []){
- q[(dp-1)/2]};
+ 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,
@@ -210,56 +270,93 @@ static int video_to_output_surface(struct vo *vo)
vdp_st = vdp->video_mixer_render(vc->video_mixer, VDP_INVALID_HANDLE,
0, field, 2, past_fields,
- vc->deint_surfaces[dp/2], 1, future_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;
}
-static void add_new_video_surface(struct vo *vo, VdpVideoSurface surface,
- struct mp_image *reserved_mpi, double pts)
+static void get_buffered_frame(struct vo *vo, bool eof)
{
struct vdpctx *vc = vo->priv;
- if (reserved_mpi)
- reserved_mpi->usage_count++;
- if (vc->deint_mpi[2])
- vc->deint_mpi[2]->usage_count--;
-
- for (int i = 2; i > 0; i--) {
- vc->deint_mpi[i] = vc->deint_mpi[i - 1];
- vc->deint_surfaces[i] = vc->deint_surfaces[i - 1];
- vc->deint_pts[i] = vc->deint_pts[i - 1];
- }
- vc->deint_mpi[0] = reserved_mpi;
- vc->deint_surfaces[0] = surface;
- vc->deint_pts[0] = pts;
+ 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;
- vo->next_pts = pts;
- if (vc->deint >= 2 && vc->deint_queue_pos >= 0) {
- vc->deint_queue_pos = 2;
- double diff = vc->deint_pts[0] - vc->deint_pts[1];
+
+ // 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)
- vo->next_pts = (vc->deint_pts[0] + vc->deint_pts[1]) / 2;
+ intermediate_pts = (bv[idx].pts + bv[idx - 1].pts) / 2;
else
- vo->next_pts = vc->deint_pts[1];
- } else
- vc->deint_queue_pos = 1;
+ 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;
+ }
+ }
+
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 = -1;
- for (int i = 0; i < 3; i++) {
- vc->deint_surfaces[i] = VDP_INVALID_HANDLE;
- if (vc->deint_mpi[i])
- vc->deint_mpi[i]->usage_count--;
- vc->deint_mpi[i] = NULL;
+ 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,
+ };
}
}
@@ -280,8 +377,8 @@ static void resize(struct vo *vo)
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 = src_rect.top;
- vc->src_rect_vid.y1 = src_rect.bottom;
+ 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
@@ -290,6 +387,7 @@ static void resize(struct vo *vo)
#endif
vo_osd_changed(OSDTYPE_OSD);
+ 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) {
@@ -315,10 +413,11 @@ static void resize(struct vo *vo)
mp_msg(MSGT_VO, MSGL_DBG2, "OUT CREATE: %u\n",
vc->output_surfaces[i]);
}
+ vc->num_shown_frames = 0;
}
- if (vc->paused && vc->visible_buf)
+ if (vc->paused && had_frames)
if (video_to_output_surface(vo) >= 0)
- flip_page(vo);
+ flip_page_timed(vo, 0, -1);
}
static void preemption_callback(VdpDevice device, void *context)
@@ -409,15 +508,76 @@ static int win_x11_init_vdpau_flip_queue(struct vo *vo)
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 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]);
+
+ VdpCSCMatrix matrix;
+ vdp_st = vdp->generate_csc_matrix(&vc->procamp, vdp_colors[csp], &matrix);
+ CHECK_ST_WARNING("Error when generating CSC matrix");
+
+ 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(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 5
+#define MAX_NUM_FEATURES 6
int i;
VdpStatus vdp_st;
@@ -457,6 +617,25 @@ static int create_vdp_mixer(struct vo *vo, VdpChromaType vdp_chroma_type)
features[feature_count++] = VDP_VIDEO_MIXER_FEATURE_NOISE_REDUCTION;
if (vc->sharpen)
features[feature_count++] = VDP_VIDEO_MIXER_FEATURE_SHARPNESS;
+ 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(vc->vdp_device, feature_count, features,
VDP_NUM_MIXER_PARAMETER,
@@ -482,6 +661,7 @@ static int create_vdp_mixer(struct vo *vo, VdpChromaType vdp_chroma_type)
skip_chroma_attrib,
skip_chroma_value_ptr);
+ update_csc_matrix(vo);
return 0;
}
@@ -498,11 +678,7 @@ static void free_video_specific(struct vo *vo)
vc->decoder = VDP_INVALID_HANDLE;
vc->decoder_max_refs = -1;
- for (i = 0; i < 2; i++)
- if (vc->deint_mpi[i]) {
- vc->deint_mpi[i]->usage_count--;
- vc->deint_mpi[i] = NULL;
- }
+ forget_frames(vo);
for (i = 0; i < MAX_VIDEO_SURFACES; i++) {
if (vc->surface_render[i].surface != VDP_INVALID_HANDLE) {
@@ -545,6 +721,11 @@ static int create_vdp_decoder(struct vo *vo, int max_refs)
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(vc->vdp_device, vdp_decoder_profile,
vc->vid_width, vc->vid_height, max_refs,
@@ -559,7 +740,7 @@ static int create_vdp_decoder(struct vo *vo, int max_refs)
return 1;
}
-int initialize_vdpau_objects(struct vo *vo)
+static int initialize_vdpau_objects(struct vo *vo)
{
struct vdpctx *vc = vo->priv;
struct vdp_functions *vdp = vc->vdp;
@@ -596,7 +777,6 @@ int initialize_vdpau_objects(struct vo *vo)
&vc->eosd_surface.max_width,
&vc->eosd_surface.max_height);
CHECK_ST_WARNING("Query to get max EOSD surface size failed");
- vc->surface_num = 0;
forget_frames(vo);
resize(vo);
return 0;
@@ -621,7 +801,7 @@ static void mark_vdpau_objects_uninitialized(struct vo *vo)
};
vc->output_surface_width = vc->output_surface_height = -1;
vc->eosd_render_count = 0;
- vc->visible_buf = false;
+ vc->num_shown_frames = 0;
}
static int handle_preemption(struct vo *vo)
@@ -676,9 +856,15 @@ static int config(struct vo *vo, uint32_t width, uint32_t height,
if (handle_preemption(vo) < 0)
return -1;
+
+ 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
+ vc->colorspace = vc->user_colorspace - 1;
free_video_specific(vo);
if (IMGFMT_IS_VDPAU(vc->image_format) && !create_vdp_decoder(vo, 2))
return -1;
@@ -740,7 +926,7 @@ static void check_events(struct vo *vo)
resize(vo);
else if (e & VO_EVENT_EXPOSE && vc->paused) {
/* did we already draw a buffer */
- if (vc->visible_buf) {
+ if (vc->num_shown_frames) {
/* redraw the last visible buffer */
VdpStatus vdp_st;
int last_surface = (vc->surface_num + NUM_OUTPUT_SURFACES - 1)
@@ -1058,23 +1244,135 @@ static void draw_osd(struct vo *vo, struct osd_state *osd)
vc->vid_height, draw_osd_I8A8, vo);
}
-static void flip_page(struct vo *vo)
+static void wait_for_previous_frame(struct vo *vo)
+{
+ struct vdpctx *vc = vo->priv;
+ struct vdp_functions *vdp = vc->vdp;
+ VdpStatus vdp_st;
+
+ if (vc->num_shown_frames < 2)
+ return;
+
+ 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;
+}
+
+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 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;
+ uint32_t vsync_interval = vc->vsync_interval;
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, 0);
+ 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->visible_buf = true;
+ 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,
@@ -1144,7 +1442,7 @@ static void draw_image(struct vo *vo, mp_image_t *mpi, double pts)
VdpStatus vdp_st;
void *destdata[3] = {mpi->planes[0], mpi->planes[2], mpi->planes[1]};
rndr = get_surface(vo, vc->deint_counter);
- vc->deint_counter = (vc->deint_counter + 1) % 3;
+ vc->deint_counter = (vc->deint_counter + 1) % NUM_BUFFERED_VIDEO;
if (vc->image_format == IMGFMT_NV12)
destdata[1] = destdata[2];
vdp_st =
@@ -1169,18 +1467,6 @@ static void draw_image(struct vo *vo, mp_image_t *mpi, double pts)
return;
}
-static void get_buffered_frame(struct vo *vo, bool eof)
-{
- struct vdpctx *vc = vo->priv;
-
- if (vc->deint_queue_pos < 2)
- return;
- vc->deint_queue_pos = 1;
- video_to_output_surface(vo);
- vo->next_pts = vc->deint_pts[0];
- vo->frame_loaded = true;
-}
-
static uint32_t get_image(struct vo *vo, mp_image_t *mpi)
{
struct vdpctx *vc = vo->priv;
@@ -1213,7 +1499,7 @@ 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_EOSD_UNSCALED | VFCAP_FLIP;
switch (format) {
case IMGFMT_YV12:
case IMGFMT_I420:
@@ -1227,6 +1513,7 @@ static int query_format(uint32_t format)
case IMGFMT_VDPAU_H264:
case IMGFMT_VDPAU_WMV3:
case IMGFMT_VDPAU_VC1:
+ case IMGFMT_VDPAU_MPEG4:
return default_flags;
}
return 0;
@@ -1282,30 +1569,13 @@ static void uninit(struct vo *vo)
#endif
vo_x11_uninit(vo);
+ // 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"
- ;
-
static int preinit(struct vo *vo, const char *arg)
{
int i;
@@ -1319,16 +1589,25 @@ static int preinit(struct vo *vo, const char *arg)
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 (vc->hqscaling < 0 || vc->hqscaling > 9) {
+ mp_msg(MSGT_VO, MSGL_FATAL, "[vdpau] Invalid value for suboption "
+ "hqscaling\n");
return -1;
}
if (vc->deint)
@@ -1398,11 +1677,6 @@ static int set_equalizer(struct vo *vo, const char *name, int value)
{
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};
if (!strcasecmp(name, "brightness"))
vc->procamp.brightness = value / 100.0;