disp: new option --fps to maintain playback rate
authorVincent Stehlé <v-stehle@ti.com>
Fri, 10 Aug 2012 14:45:50 +0000 (16:45 +0200)
committerVincent Stehlé <v-stehle@ti.com>
Fri, 17 Aug 2012 14:56:28 +0000 (16:56 +0200)
Add a new rate control mechanism to the generic display, to allow forcing the
playback rate from command line.

We add rate control calls in the generic display functions disp_post_buffer()
and disp_post_vid_buffer(). As this is in the generic display part, x11 and kms
(and future) displays benefit from that mechanism.

We use the mark() and usleep() functions for the rate control implementation,
and we do also filter a bit the sleep duration, to avoid being too "choppy".

Signed-off-by: Vincent Stehlé <v-stehle@ti.com>
util/util.c
util/util.h

index 36c4ad90b57fa2628464117551ae82902d7400b1..3b3ca86b0812f1ea18dab2a19a1e8224db266332 100644 (file)
@@ -35,6 +35,9 @@ void disp_x11_close(struct display *disp);
 void
 disp_usage(void)
 {
+       MSG("Generic Display options:");
+       MSG("\t--fps <fps>\tforce playback rate (0 means \"do not force\")");
+
 #ifdef HAVE_X11
        disp_x11_usage();
 #endif
@@ -45,11 +48,29 @@ struct display *
 disp_open(int argc, char **argv)
 {
        struct display *disp;
+       int i, fps = 0;
+
+       for (i = 1; i < argc; i++) {
+               if (!argv[i]) {
+                       continue;
+               }
+               if (!strcmp("--fps", argv[i])) {
+                       argv[i++] = NULL;
+
+                       if (sscanf(argv[i], "%d", &fps) != 1) {
+                               ERROR("invalid arg: %s", argv[i]);
+                               return NULL;
+                       }
+
+                       MSG("Forcing playback rate at %d fps.", fps);
+                       argv[i] = NULL;
+               }
+       }
 
 #ifdef HAVE_X11
        disp = disp_x11_open(argc, argv);
        if (disp)
-               return disp;
+               goto out;
 #endif
 
        disp = disp_kms_open(argc, argv);
@@ -58,6 +79,8 @@ disp_open(int argc, char **argv)
                ERROR("unable to create display");
        }
 
+out:
+       disp->rtctl.fps = fps;
        return disp;
 }
 
@@ -111,6 +134,56 @@ disp_put_vid_buffer(struct display *disp, struct buffer *buf)
        list_add(&buf->unlocked, &disp->unlocked);
 }
 
+/* Maintain playback rate if fps > 0. */
+static void maintain_playback_rate(struct rate_control *p)
+{
+       long usecs_since_last_frame;
+       int usecs_between_frames, usecs_to_sleep;
+
+       if (p->fps <= 0)
+               return;
+
+       usecs_between_frames = 1000000 / p->fps;
+       usecs_since_last_frame = mark(&p->last_frame_mark);
+       MSG("fps: %.02f", 1000000.0 / usecs_since_last_frame);
+       usecs_to_sleep = usecs_between_frames - usecs_since_last_frame + p->usecs_to_sleep;
+
+       if (usecs_to_sleep < 0)
+               usecs_to_sleep = 0;
+
+       /* mark() has a limitation that >1s time deltas will make the whole
+        * loop diverge. Workaround that limitation by clamping our desired sleep time
+        * to a maximum. TODO: Remove when mark() is in better shape. */
+       if (usecs_to_sleep >= 1000000)
+               usecs_to_sleep = 999999;
+
+       /* We filter a bit our rate adaptation, to avoid being too "choppy".
+        * Adjust the "alpha" value as needed. */
+       p->usecs_to_sleep = ((67 * p->usecs_to_sleep) + (33 * usecs_to_sleep)) / 100;
+
+       if (p->usecs_to_sleep >= 1) {
+               MSG("sleeping %dus", p->usecs_to_sleep);
+               usleep(p->usecs_to_sleep);
+       }
+}
+
+/* flip to / post the specified buffer */
+int
+disp_post_buffer(struct display *disp, struct buffer *buf)
+{
+       maintain_playback_rate(&disp->rtctl);
+       return disp->post_buffer(disp, buf);
+}
+
+/* flip to / post the specified video buffer */
+int
+disp_post_vid_buffer(struct display *disp, struct buffer *buf,
+               uint32_t x, uint32_t y, uint32_t w, uint32_t h)
+{
+       maintain_playback_rate(&disp->rtctl);
+       return disp->post_vid_buffer(disp, buf, x, y, w, h);
+}
+
 struct buffer *
 disp_get_fb(struct display *disp)
 {
index 53b6e1cd1689cc38422fdaceadf0dcacab1f6360..505bdcb465561c8023184feeac40385df866d5b6 100644 (file)
@@ -56,11 +56,20 @@ struct buffer {
        struct list unlocked;
 };
 
+/* State variables, used to maintain the playback rate. */
+struct rate_control {
+       int fps;                /* When > zero, we maintain playback rate. */
+       long last_frame_mark;   /* The time when the last frame was displayed,
+                                * as returned by the mark() function. */
+       int usecs_to_sleep;     /* Number of useconds we have slep last frame. */
+};
+
 struct display {
        int fd;
        uint32_t width, height;
        struct omap_device *dev;
        struct list unlocked;
+       struct rate_control rtctl;
 
        struct buffer ** (*get_buffers)(struct display *disp, uint32_t n);
        struct buffer ** (*get_vid_buffers)(struct display *disp,
@@ -93,19 +102,13 @@ struct buffer ** disp_get_vid_buffers(struct display *disp, uint32_t n,
                uint32_t fourcc, uint32_t w, uint32_t h);
 
 /* flip to / post the specified buffer */
-static inline int
-disp_post_buffer(struct display *disp, struct buffer *buf)
-{
-       return disp->post_buffer(disp, buf);
-}
+int
+disp_post_buffer(struct display *disp, struct buffer *buf);
 
 /* flip to / post the specified video buffer */
-static inline int
+int
 disp_post_vid_buffer(struct display *disp, struct buffer *buf,
-               uint32_t x, uint32_t y, uint32_t w, uint32_t h)
-{
-       return disp->post_vid_buffer(disp, buf, x, y, w, h);
-}
+               uint32_t x, uint32_t y, uint32_t w, uint32_t h);
 
 /* allocate a buffer from pool created by disp_get_vid_buffers() */
 struct buffer * disp_get_vid_buffer(struct display *disp);