[vlc-devel] [PATCH v2 2/2] mmal/vout: Apply phase offset to match vsync period

Julian Scheel julian at jusst.de
Thu Oct 9 12:08:57 CEST 2014


As the latency target alone is not ensuring that we start with a sane phase
shift between vsync and drawing, we read back the phase shift provided by the
mmal rendering statistics and use it to compute a phase offset which we apply
to the picture date, so that the core shifts the display call accordingly.
Doing this frequently allows us to resync if a distortion or display reset
caused the offset to break.
This is only enabled if mmal-adjust-refreshrate is enabled, because the whole
vsync synchronisation makes only sense if display and videorate match.

Signed-off-by: Julian Scheel <julian at jusst.de>
---
Changes in v2:
 - Do not require a core extension but apply the offset in vd_prepare
 - Reset the mmal latency target when the offset is applied to avoid
   overshoots of the latency target adjustment filter

 modules/hw/mmal/vout.c | 81 +++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 80 insertions(+), 1 deletion(-)

diff --git a/modules/hw/mmal/vout.c b/modules/hw/mmal/vout.c
index e6fb8c3..dd2d6d1 100644
--- a/modules/hw/mmal/vout.c
+++ b/modules/hw/mmal/vout.c
@@ -58,6 +58,10 @@
 #define MMAL_ADJUST_REFRESHRATE_TEXT N_("Adjust HDMI refresh rate to the video.")
 #define MMAL_ADJUST_REFRESHRATE_LONGTEXT N_("Adjust HDMI refresh rate to the video.")
 
+/* Ideal rendering phase target is at rough 25% of frame duration */
+#define PHASE_OFFSET_TARGET ((double)0.25)
+#define PHASE_CHECK_INTERVAL 100
+
 static int Open(vlc_object_t *);
 static void Close(vlc_object_t *);
 
@@ -110,6 +114,10 @@ struct vout_display_sys_t {
     unsigned display_height;
     bool need_configure_display;
 
+    bool adjust_refresh_rate;
+    int next_phase_check;
+    int phase_offset;
+
     int layer;
     bool opaque;
 };
@@ -126,6 +134,8 @@ static int configure_display(vout_display_t *vd, const vout_display_cfg_t *cfg,
 
 /* VLC vout display callbacks */
 static picture_pool_t *vd_pool(vout_display_t *vd, unsigned count);
+static void vd_prepare(vout_display_t *vd, picture_t *picture,
+                subpicture_t *subpicture);
 static void vd_display(vout_display_t *vd, picture_t *picture,
                 subpicture_t *subpicture);
 static int vd_control(vout_display_t *vd, int query, va_list args);
@@ -152,6 +162,7 @@ static void dmx_region_update(struct dmx_region_t *dmx_region,
 static void dmx_region_delete(struct dmx_region_t *dmx_region,
                 DISPMANX_UPDATE_HANDLE_T update);
 static void show_background(vout_display_t *vd, bool enable);
+static void maintain_phase_sync(vout_display_t *vd);
 
 static int Open(vlc_object_t *object)
 {
@@ -276,6 +287,7 @@ static int Open(vlc_object_t *object)
     vlc_mutex_init(&sys->manage_mutex);
 
     vd->pool = vd_pool;
+    vd->prepare = vd_prepare;
     vd->display = vd_display;
     vd->control = vd_control;
     vd->manage = vd_manage;
@@ -403,7 +415,8 @@ static int configure_display(vout_display_t *vd, const vout_display_cfg_t *cfg,
     }
 
     show_background(vd, cfg->is_fullscreen);
-    if (var_InheritBool(vd, MMAL_ADJUST_REFRESHRATE_NAME)) {
+    sys->adjust_refresh_rate = var_InheritBool(vd, MMAL_ADJUST_REFRESHRATE_NAME);
+    if (sys->adjust_refresh_rate) {
         adjust_refresh_rate(vd);
         set_latency_target(vd, true);
     }
@@ -500,6 +513,20 @@ out:
     return sys->picture_pool;
 }
 
+static void vd_prepare(vout_display_t *vd, picture_t *picture,
+                subpicture_t *subpicture)
+{
+    vout_display_sys_t *sys = vd->sys;
+    picture_sys_t *pic_sys = picture->p_sys;
+
+    if (!sys->adjust_refresh_rate || pic_sys->displayed)
+        return;
+
+    /* Apply the required phase_offset to the picture, so that vd_display()
+     * will be called at the corrected time from the core */
+    picture->date += sys->phase_offset;
+}
+
 static void vd_display(vout_display_t *vd, picture_t *picture,
                 subpicture_t *subpicture)
 {
@@ -535,6 +562,10 @@ static void vd_display(vout_display_t *vd, picture_t *picture,
 
     if (subpicture)
         subpicture_Delete(subpicture);
+
+    if (sys->next_phase_check == 0 && sys->adjust_refresh_rate)
+        maintain_phase_sync(vd);
+    sys->next_phase_check = (sys->next_phase_check + 1) % PHASE_CHECK_INTERVAL;
 }
 
 static int vd_control(vout_display_t *vd, int query, va_list args)
@@ -902,6 +933,54 @@ static void dmx_region_delete(struct dmx_region_t *dmx_region,
     free(dmx_region);
 }
 
+static void maintain_phase_sync(vout_display_t *vd)
+{
+    MMAL_PARAMETER_VIDEO_RENDER_STATS_T render_stats = {
+        .hdr = { MMAL_PARAMETER_VIDEO_RENDER_STATS, sizeof(render_stats) },
+    };
+    int32_t frame_duration = 1000000 /
+        ((double)vd->fmt.i_frame_rate /
+        vd->fmt.i_frame_rate_base);
+    vout_display_sys_t *sys = vd->sys;
+    int32_t phase_offset;
+    MMAL_STATUS_T status;
+
+    status = mmal_port_parameter_get(sys->input, &render_stats.hdr);
+    if (status != MMAL_SUCCESS) {
+        msg_Err(vd, "Failed to read render stats on control port %s (status=%"PRIx32" %s)",
+                        sys->input->name, status, mmal_status_to_string(status));
+        return;
+    }
+
+    if (render_stats.valid) {
+#ifndef NDEBUG
+        msg_Dbg(vd, "render_stats: match: %u, period: %u ms, phase: %u ms, hvs: %u",
+                render_stats.match, render_stats.period / 1000, render_stats.phase / 1000,
+                render_stats.hvs_status);
+#endif
+
+        if (render_stats.phase > 0.1 * frame_duration &&
+                render_stats.phase < 0.75 * frame_duration)
+            return;
+
+        phase_offset = frame_duration * PHASE_OFFSET_TARGET - render_stats.phase;
+        if (phase_offset < 0)
+            phase_offset += frame_duration;
+        else
+            phase_offset %= frame_duration;
+
+        sys->phase_offset += phase_offset;
+        sys->phase_offset %= frame_duration;
+        msg_Dbg(vd, "Apply phase offset of %"PRId32" ms (total offset %"PRId32" ms)",
+                phase_offset / 1000, sys->phase_offset / 1000);
+
+        /* Reset the latency target, so that it does not get confused
+         * by the jump in the offset */
+        set_latency_target(vd, false);
+        set_latency_target(vd, true);
+    }
+}
+
 static void show_background(vout_display_t *vd, bool enable)
 {
     vout_display_sys_t *sys = vd->sys;
-- 
2.1.2




More information about the vlc-devel mailing list