kmssink: calculate display ratio

Get the aspect ratio given the information provided by libdrm, and with it
calculate the display ratio.

https://bugzilla.gnome.org/show_bug.cgi?id=761059
This commit is contained in:
Víctor Manuel Jáquez Leal 2016-01-31 13:12:34 +01:00
parent 620e1d2fcd
commit 1aee6cdc25
3 changed files with 125 additions and 2 deletions

View file

@ -381,6 +381,12 @@ gst_kms_sink_start (GstBaseSink * bsink)
self->hdisplay = crtc->mode.hdisplay; self->hdisplay = crtc->mode.hdisplay;
self->vdisplay = crtc->mode.vdisplay; self->vdisplay = crtc->mode.vdisplay;
self->mm_width = conn->mmWidth;
self->mm_height = conn->mmHeight;
GST_INFO_OBJECT (self, "display size: pixels = %dx%d / millimeters = %dx%d",
self->hdisplay, self->vdisplay, self->mm_width, self->mm_height);
ret = TRUE; ret = TRUE;
bail: bail:
@ -534,6 +540,57 @@ config_failed:
} }
} }
static gboolean
gst_kms_sink_calculate_display_ratio (GstKMSSink * self, GstVideoInfo * vinfo)
{
guint dar_n, dar_d;
guint video_width, video_height;
guint video_par_n, video_par_d;
guint dpy_par_n, dpy_par_d;
video_width = GST_VIDEO_INFO_WIDTH (vinfo);
video_height = GST_VIDEO_INFO_HEIGHT (vinfo);
video_par_n = GST_VIDEO_INFO_PAR_N (vinfo);
video_par_d = GST_VIDEO_INFO_PAR_D (vinfo);
gst_video_calculate_device_ratio (self->hdisplay, self->vdisplay,
self->mm_width, self->mm_height, &dpy_par_n, &dpy_par_d);
if (!gst_video_calculate_display_ratio (&dar_n, &dar_d, video_width,
video_height, video_par_n, video_par_d, dpy_par_n, dpy_par_d))
return FALSE;
GST_DEBUG_OBJECT (self, "video calculated display ratio: %d/%d", dar_n,
dar_d);
/* now find a width x height that respects this display ratio.
* prefer those that have one of w/h the same as the incoming video
* using wd / hd = dar_n / dar_d */
/* start with same height, because of interlaced video */
/* check hd / dar_d is an integer scale factor, and scale wd with the PAR */
if (video_height % dar_d == 0) {
GST_DEBUG_OBJECT (self, "keeping video height");
GST_VIDEO_SINK_WIDTH (self) = (guint)
gst_util_uint64_scale_int (video_height, dar_n, dar_d);
GST_VIDEO_SINK_HEIGHT (self) = video_height;
} else if (video_width % dar_n == 0) {
GST_DEBUG_OBJECT (self, "keeping video width");
GST_VIDEO_SINK_WIDTH (self) = video_width;
GST_VIDEO_SINK_HEIGHT (self) = (guint)
gst_util_uint64_scale_int (video_width, dar_d, dar_n);
} else {
GST_DEBUG_OBJECT (self, "approximating while keeping video height");
GST_VIDEO_SINK_WIDTH (self) = (guint)
gst_util_uint64_scale_int (video_height, dar_n, dar_d);
GST_VIDEO_SINK_HEIGHT (self) = video_height;
}
GST_DEBUG_OBJECT (self, "scaling to %dx%d", GST_VIDEO_SINK_WIDTH (self),
GST_VIDEO_SINK_HEIGHT (self));
return TRUE;
}
static gboolean static gboolean
gst_kms_sink_set_caps (GstBaseSink * bsink, GstCaps * caps) gst_kms_sink_set_caps (GstBaseSink * bsink, GstCaps * caps)
{ {
@ -546,8 +603,8 @@ gst_kms_sink_set_caps (GstBaseSink * bsink, GstCaps * caps)
if (!gst_video_info_from_caps (&vinfo, caps)) if (!gst_video_info_from_caps (&vinfo, caps))
goto invalid_format; goto invalid_format;
GST_VIDEO_SINK_WIDTH (self) = GST_VIDEO_INFO_WIDTH (&vinfo); if (!gst_kms_sink_calculate_display_ratio (self, &vinfo))
GST_VIDEO_SINK_HEIGHT (self) = GST_VIDEO_INFO_HEIGHT (&vinfo); goto no_disp_ratio;
if (GST_VIDEO_SINK_WIDTH (self) <= 0 || GST_VIDEO_SINK_HEIGHT (self) <= 0) if (GST_VIDEO_SINK_WIDTH (self) <= 0 || GST_VIDEO_SINK_HEIGHT (self) <= 0)
goto invalid_size; goto invalid_size;
@ -586,6 +643,13 @@ invalid_size:
("Invalid image size.")); ("Invalid image size."));
return FALSE; return FALSE;
} }
no_disp_ratio:
{
GST_ELEMENT_ERROR (self, CORE, NEGOTIATION, (NULL),
("Error calculating the output display ratio of the video."));
return FALSE;
}
no_pool: no_pool:
{ {
/* Already warned in create_pool */ /* Already warned in create_pool */

View file

@ -121,3 +121,56 @@ gst_kms_sink_caps_template_fill (void)
} }
return gst_caps_simplify (caps); return gst_caps_simplify (caps);
} }
static const gint device_par_map[][2] = {
{1, 1}, /* regular screen */
{16, 15}, /* PAL TV */
{11, 10}, /* 525 line Rec.601 video */
{54, 59}, /* 625 line Rec.601 video */
{64, 45}, /* 1280x1024 on 16:9 display */
{5, 3}, /* 1280x1024 on 4:3 display */
{4, 3} /* 800x600 on 16:9 display */
};
#define DELTA(ratio, idx, w) \
(ABS(ratio - ((gdouble)device_par_map[idx][w] / device_par_map[idx][!(w)])))
void
gst_video_calculate_device_ratio (guint dev_width, guint dev_height,
guint dev_width_mm, guint dev_height_mm,
guint * dpy_par_n, guint * dpy_par_d)
{
gdouble ratio, delta, cur_delta;
gint i, j, index, windex;
/* First, calculate the "real" ratio based on the X values; which is
* the "physical" w/h divided by the w/h in pixels of the display */
if (dev_width == 0 || dev_height == 0
|| dev_width_mm == 0 || dev_height_mm == 0)
ratio = 1.0;
else
ratio = (gdouble) (dev_width_mm * dev_height) / (dev_height_mm * dev_width);
/* Now, find the one from device_par_map[][2] with the lowest delta
* to the real one */
delta = DELTA (ratio, 0, 0);
index = 0;
windex = 0;
for (i = 1; i < G_N_ELEMENTS (device_par_map); i++) {
for (j = 0; j < 2; j++) {
cur_delta = DELTA (ratio, i, j);
if (cur_delta < delta) {
index = i;
windex = j;
delta = cur_delta;
}
}
}
if (dpy_par_n)
*dpy_par_n = device_par_map[index][windex];
if (dpy_par_d)
*dpy_par_d = device_par_map[index][windex ^ 1];
}

View file

@ -33,6 +33,12 @@ G_BEGIN_DECLS
GstVideoFormat gst_video_format_from_drm (guint32 drmfmt); GstVideoFormat gst_video_format_from_drm (guint32 drmfmt);
guint32 gst_drm_format_from_video (GstVideoFormat fmt); guint32 gst_drm_format_from_video (GstVideoFormat fmt);
GstCaps * gst_kms_sink_caps_template_fill (void); GstCaps * gst_kms_sink_caps_template_fill (void);
void gst_video_calculate_device_ratio (guint dev_width,
guint dev_height,
guint dev_width_mm,
guint dev_height_mm,
guint * dpy_par_n,
guint * dpy_par_d);
G_END_DECLS G_END_DECLS