[FFmpeg-devel] [PATCH] lavu: add av_gettime_monotonic() and use it.

Olivier Langlois olivier at trillion01.com
Fri Apr 25 23:58:32 CEST 2014


Also add similar av_usleep_monotonic().

These functions are using the POSIX clock_gettime() function with the
CLOCK_MONOTONIC clock id. If these are not present on the targeted
platform, the new functions will fallback on using the original realtime functions
av_gettime() and av_usleep().

Monotonic support can be added on other platforms with their
equivalent native system API eventually if possible.

Whenever time is requested to measure relative time, the monotonic clock,
when available, is superior to the system realtime clock because it is
not affected by discontinuous jumps in the system time

In a future step, offering the flexibility to let the user choose between
rt and monotonic clock for avdevices packets will be investigated.

It is very easy to experience the issues that this patch attempt to address
by rewinding back in the past the system time while ffmpeg is running.

this is breaking the ffmpeg report printing (ffmepg.c:print_report()) and
the the rate emulator functionality (-re) without the patch.

Signed-off-by: Olivier Langlois <olivier at trillion01.com>
---
 cmdutils_opencl.c        |  4 ++--
 configure                |  2 ++
 ffmpeg.c                 | 10 +++++-----
 ffplay.c                 | 26 +++++++++++++-------------
 libavcodec/dct-test.c    |  8 ++++----
 libavcodec/fft-test.c    |  4 ++--
 libavcodec/motion-test.c |  4 ++--
 libavdevice/v4l2.c       | 10 ----------
 libavformat/avio.c       |  4 ++--
 libavformat/network.c    |  4 ++--
 libavformat/sapenc.c     |  2 +-
 libavutil/time.c         | 32 +++++++++++++++++++++++++++++++-
 libavutil/time.h         | 20 ++++++++++++++++++++
 tools/aviocat.c          |  6 +++---
 14 files changed, 89 insertions(+), 47 deletions(-)

diff --git a/cmdutils_opencl.c b/cmdutils_opencl.c
index 2a04db9..d0ced05 100644
--- a/cmdutils_opencl.c
+++ b/cmdutils_opencl.c
@@ -181,12 +181,12 @@ static int64_t run_opencl_bench(AVOpenCLExternalEnv *ext_opencl_env)
     OCLCHECK(clSetKernelArg, kernel, arg++, sizeof(cl_int), &width);
     OCLCHECK(clSetKernelArg, kernel, arg++, sizeof(cl_int), &height);
 
-    start = av_gettime();
+    start = av_gettime_monotonic();
     for (i = 0; i < OPENCL_NB_ITER; i++)
         OCLCHECK(clEnqueueNDRangeKernel, ext_opencl_env->command_queue, kernel, 2, NULL,
                  global_work_size_2d, local_work_size_2d, 0, NULL, NULL);
     clFinish(ext_opencl_env->command_queue);
-    ret = (av_gettime() - start)/OPENCL_NB_ITER;
+    ret = (av_gettime_monotonic() - start)/OPENCL_NB_ITER;
 end:
     if (kernel)
         clReleaseKernel(kernel);
diff --git a/configure b/configure
index 7f34e9a..99eeb65 100755
--- a/configure
+++ b/configure
@@ -1657,6 +1657,7 @@ SYSTEM_FUNCS="
     access
     aligned_malloc
     clock_gettime
+    clock_nanosleep
     closesocket
     CommandLineToArgvW
     CryptGenRandom
@@ -4467,6 +4468,7 @@ check_func  ${malloc_prefix}posix_memalign      && enable posix_memalign
 
 check_func  access
 check_func  clock_gettime || { check_func clock_gettime -lrt && add_extralibs -lrt; }
+check_func  clock_nanosleep || { check_func clock_nanosleep && add_extralibs -lrt; }
 check_func  fcntl
 check_func  fork
 check_func  gethrtime
diff --git a/ffmpeg.c b/ffmpeg.c
index 9edc721..f670b92 100644
--- a/ffmpeg.c
+++ b/ffmpeg.c
@@ -2380,7 +2380,7 @@ static int transcode_init(void)
         InputFile *ifile = input_files[i];
         if (ifile->rate_emu)
             for (j = 0; j < ifile->nb_streams; j++)
-                input_streams[j + ifile->ist_index]->start = av_gettime();
+                input_streams[j + ifile->ist_index]->start = av_gettime_monotonic();
     }
 
     /* output stream init */
@@ -3173,7 +3173,7 @@ static int get_input_packet(InputFile *f, AVPacket *pkt)
         for (i = 0; i < f->nb_streams; i++) {
             InputStream *ist = input_streams[f->ist_index + i];
             int64_t pts = av_rescale(ist->dts, 1000000, AV_TIME_BASE);
-            int64_t now = av_gettime() - ist->start;
+            int64_t now = av_gettime_monotonic() - ist->start;
             if (pts > now)
                 return AVERROR(EAGAIN);
         }
@@ -3539,7 +3539,7 @@ static int transcode(void)
         av_log(NULL, AV_LOG_INFO, "Press [q] to stop, [?] for help\n");
     }
 
-    timer_start = av_gettime();
+    timer_start = av_gettime_monotonic();
 
 #if HAVE_PTHREADS
     if ((ret = init_input_threads()) < 0)
@@ -3547,7 +3547,7 @@ static int transcode(void)
 #endif
 
     while (!received_sigterm) {
-        int64_t cur_time= av_gettime();
+        int64_t cur_time= av_gettime_monotonic();
 
         /* if 'q' pressed, exits */
         if (stdin_interaction)
@@ -3594,7 +3594,7 @@ static int transcode(void)
     }
 
     /* dump report by using the first video and audio streams */
-    print_report(1, timer_start, av_gettime());
+    print_report(1, timer_start, av_gettime_monotonic());
 
     /* close each encoder */
     for (i = 0; i < nb_output_streams; i++) {
diff --git a/ffplay.c b/ffplay.c
index 86b9126..04d805c 100644
--- a/ffplay.c
+++ b/ffplay.c
@@ -899,7 +899,7 @@ static void video_audio_display(VideoState *s)
         /* to be more precise, we take into account the time spent since
            the last buffer computation */
         if (audio_callback_time) {
-            time_diff = av_gettime() - audio_callback_time;
+            time_diff = av_gettime_monotonic() - audio_callback_time;
             delay -= (time_diff * s->audio_tgt.freq) / 1000000;
         }
 
@@ -1132,7 +1132,7 @@ static double get_clock(Clock *c)
     if (c->paused) {
         return c->pts;
     } else {
-        double time = av_gettime() / 1000000.0;
+        double time = av_gettime_monotonic() / 1000000.0;
         return c->pts_drift + time - (time - c->last_updated) * (1.0 - c->speed);
     }
 }
@@ -1147,7 +1147,7 @@ static void set_clock_at(Clock *c, double pts, int serial, double time)
 
 static void set_clock(Clock *c, double pts, int serial)
 {
-    double time = av_gettime() / 1000000.0;
+    double time = av_gettime_monotonic() / 1000000.0;
     set_clock_at(c, pts, serial, time);
 }
 
@@ -1240,7 +1240,7 @@ static void stream_seek(VideoState *is, int64_t pos, int64_t rel, int seek_by_by
 static void stream_toggle_pause(VideoState *is)
 {
     if (is->paused) {
-        is->frame_timer += av_gettime() / 1000000.0 + is->vidclk.pts_drift - is->vidclk.pts;
+        is->frame_timer += av_gettime_monotonic() / 1000000.0 + is->vidclk.pts_drift - is->vidclk.pts;
         if (is->read_pause_return != AVERROR(ENOSYS)) {
             is->vidclk.paused = 0;
         }
@@ -1355,7 +1355,7 @@ static void video_refresh(void *opaque, double *remaining_time)
         check_external_clock_speed(is);
 
     if (!display_disable && is->show_mode != SHOW_MODE_VIDEO && is->audio_st) {
-        time = av_gettime() / 1000000.0;
+        time = av_gettime_monotonic() / 1000000.0;
         if (is->force_refresh || is->last_vis_time + rdftspeed < time) {
             video_display(is);
             is->last_vis_time = time;
@@ -1386,7 +1386,7 @@ retry:
             }
 
             if (lastvp->serial != vp->serial && !redisplay)
-                is->frame_timer = av_gettime() / 1000000.0;
+                is->frame_timer = av_gettime_monotonic() / 1000000.0;
 
             if (is->paused)
                 goto display;
@@ -1398,7 +1398,7 @@ retry:
             else
                 delay = compute_target_delay(last_duration, is);
 
-            time= av_gettime()/1000000.0;
+            time= av_gettime_monotonic()/1000000.0;
             if (time < is->frame_timer + delay && !redisplay) {
                 *remaining_time = FFMIN(is->frame_timer + delay - time, *remaining_time);
                 return;
@@ -1472,7 +1472,7 @@ display:
         int aqsize, vqsize, sqsize;
         double av_diff;
 
-        cur_time = av_gettime();
+        cur_time = av_gettime_monotonic();
         if (!last_time || (cur_time - last_time) >= 30000) {
             aqsize = 0;
             vqsize = 0;
@@ -1967,7 +1967,7 @@ static int video_thread(void *arg)
             goto the_end;
 
         while (ret >= 0) {
-            is->frame_last_returned_time = av_gettime() / 1000000.0;
+            is->frame_last_returned_time = av_gettime_monotonic() / 1000000.0;
 
             ret = av_buffersink_get_frame_flags(filt_out, frame, 0);
             if (ret < 0) {
@@ -1977,7 +1977,7 @@ static int video_thread(void *arg)
                 break;
             }
 
-            is->frame_last_filter_delay = av_gettime() / 1000000.0 - is->frame_last_returned_time;
+            is->frame_last_filter_delay = av_gettime_monotonic() / 1000000.0 - is->frame_last_returned_time;
             if (fabs(is->frame_last_filter_delay) > AV_NOSYNC_THRESHOLD / 10.0)
                 is->frame_last_filter_delay = 0;
             tb = filt_out->inputs[0]->time_base;
@@ -2378,7 +2378,7 @@ static void sdl_audio_callback(void *opaque, Uint8 *stream, int len)
     VideoState *is = opaque;
     int audio_size, len1;
 
-    audio_callback_time = av_gettime();
+    audio_callback_time = av_gettime_monotonic();
 
     while (len > 0) {
         if (is->audio_buf_index >= is->audio_buf_size) {
@@ -3164,7 +3164,7 @@ static void refresh_loop_wait_event(VideoState *is, SDL_Event *event) {
     double remaining_time = 0.0;
     SDL_PumpEvents();
     while (!SDL_PeepEvents(event, 1, SDL_GETEVENT, SDL_ALLEVENTS)) {
-        if (!cursor_hidden && av_gettime() - cursor_last_shown > CURSOR_HIDE_DELAY) {
+        if (!cursor_hidden && av_gettime_monotonic() - cursor_last_shown > CURSOR_HIDE_DELAY) {
             SDL_ShowCursor(0);
             cursor_hidden = 1;
         }
@@ -3318,7 +3318,7 @@ static void event_loop(VideoState *cur_stream)
                 SDL_ShowCursor(1);
                 cursor_hidden = 0;
             }
-            cursor_last_shown = av_gettime();
+            cursor_last_shown = av_gettime_monotonic();
             if (event.type == SDL_MOUSEBUTTONDOWN) {
                 x = event.button.x;
             } else {
diff --git a/libavcodec/dct-test.c b/libavcodec/dct-test.c
index 6308348..6b331f5 100644
--- a/libavcodec/dct-test.c
+++ b/libavcodec/dct-test.c
@@ -348,7 +348,7 @@ static int dct_error(const struct algo *dct, int test, int is_idct, int speed, c
     init_block(block, test, is_idct, &prng, vals);
     permute(block1, block, dct->format);
 
-    ti = av_gettime();
+    ti = av_gettime_monotonic();
     it1 = 0;
     do {
         for (it = 0; it < NB_ITS_SPEED; it++) {
@@ -357,7 +357,7 @@ static int dct_error(const struct algo *dct, int test, int is_idct, int speed, c
         }
         emms_c();
         it1 += NB_ITS_SPEED;
-        ti1 = av_gettime() - ti;
+        ti1 = av_gettime_monotonic() - ti;
     } while (ti1 < 1000000);
 
     printf("%s %s: %0.1f kdct/s\n", is_idct ? "IDCT" : "DCT", dct->name,
@@ -508,7 +508,7 @@ static void idct248_error(const char *name,
     if (!speed)
         return;
 
-    ti = av_gettime();
+    ti = av_gettime_monotonic();
     it1 = 0;
     do {
         for (it = 0; it < NB_ITS_SPEED; it++) {
@@ -518,7 +518,7 @@ static void idct248_error(const char *name,
         }
         emms_c();
         it1 += NB_ITS_SPEED;
-        ti1 = av_gettime() - ti;
+        ti1 = av_gettime_monotonic() - ti;
     } while (ti1 < 1000000);
 
     printf("%s %s: %0.1f kdct/s\n", 1 ? "IDCT248" : "DCT248", name,
diff --git a/libavcodec/fft-test.c b/libavcodec/fft-test.c
index a29896f..73ddad3 100644
--- a/libavcodec/fft-test.c
+++ b/libavcodec/fft-test.c
@@ -438,7 +438,7 @@ int main(int argc, char **argv)
         /* we measure during about 1 seconds */
         nb_its = 1;
         for(;;) {
-            time_start = av_gettime();
+            time_start = av_gettime_monotonic();
             for (it = 0; it < nb_its; it++) {
                 switch (transform) {
                 case TRANSFORM_MDCT:
@@ -464,7 +464,7 @@ int main(int argc, char **argv)
 #endif
                 }
             }
-            duration = av_gettime() - time_start;
+            duration = av_gettime_monotonic() - time_start;
             if (duration >= 1000000)
                 break;
             nb_its *= 2;
diff --git a/libavcodec/motion-test.c b/libavcodec/motion-test.c
index 53cfedb..fca7260 100644
--- a/libavcodec/motion-test.c
+++ b/libavcodec/motion-test.c
@@ -91,7 +91,7 @@ static void test_motion(const char *name,
     emms_c();
 
     /* speed test */
-    ti = av_gettime();
+    ti = av_gettime_monotonic();
     d1 = 0;
     for(it=0;it<NB_ITS;it++) {
         for(y=0;y<HEIGHT-17;y++) {
@@ -103,7 +103,7 @@ static void test_motion(const char *name,
     }
     emms_c();
     dummy = d1; /* avoid optimization */
-    ti = av_gettime() - ti;
+    ti = av_gettime_monotonic() - ti;
 
     printf("  %0.0f kop/s\n",
            (double)NB_ITS * (WIDTH - 16) * (HEIGHT - 16) /
diff --git a/libavdevice/v4l2.c b/libavdevice/v4l2.c
index c671e3a..331dc8f 100644
--- a/libavdevice/v4l2.c
+++ b/libavdevice/v4l2.c
@@ -424,16 +424,6 @@ static void mmap_release_buffer(void *opaque, uint8_t *data)
     avpriv_atomic_int_add_and_fetch(&s->buffers_queued, 1);
 }
 
-#if HAVE_CLOCK_GETTIME && defined(CLOCK_MONOTONIC)
-static int64_t av_gettime_monotonic(void)
-{
-    struct timespec tv;
-
-    clock_gettime(CLOCK_MONOTONIC, &tv);
-    return (int64_t)tv.tv_sec * 1000000 + tv.tv_nsec / 1000;
-}
-#endif
-
 static int init_convert_timestamp(AVFormatContext *ctx, int64_t ts)
 {
     struct video_data *s = ctx->priv_data;
diff --git a/libavformat/avio.c b/libavformat/avio.c
index 4edaaa6..4b70b7f 100644
--- a/libavformat/avio.c
+++ b/libavformat/avio.c
@@ -312,8 +312,8 @@ static inline int retry_transfer_wrapper(URLContext *h, uint8_t *buf,
             } else {
                 if (h->rw_timeout) {
                     if (!wait_since)
-                        wait_since = av_gettime();
-                    else if (av_gettime() > wait_since + h->rw_timeout)
+                        wait_since = av_gettime_monotonic();
+                    else if (av_gettime_monotonic() > wait_since + h->rw_timeout)
                         return AVERROR(EIO);
                 }
                 av_usleep(1000);
diff --git a/libavformat/network.c b/libavformat/network.c
index 5e574e3..c972ce9 100644
--- a/libavformat/network.c
+++ b/libavformat/network.c
@@ -163,8 +163,8 @@ int ff_network_wait_fd_timeout(int fd, int write, int64_t timeout, AVIOInterrupt
             return ret;
         if (timeout > 0) {
             if (!wait_start)
-                wait_start = av_gettime();
-            else if (av_gettime() - wait_start > timeout)
+                wait_start = av_gettime_monotonic();
+            else if (av_gettime_monotonic() - wait_start > timeout)
                 return AVERROR(ETIMEDOUT);
         }
     }
diff --git a/libavformat/sapenc.c b/libavformat/sapenc.c
index 738e8b8..d405ab3 100644
--- a/libavformat/sapenc.c
+++ b/libavformat/sapenc.c
@@ -245,7 +245,7 @@ static int sap_write_packet(AVFormatContext *s, AVPacket *pkt)
 {
     AVFormatContext *rtpctx;
     struct SAPState *sap = s->priv_data;
-    int64_t now = av_gettime();
+    int64_t now = av_gettime_monotonic();
 
     if (!sap->last_time || now - sap->last_time > 5000000) {
         int ret = ffurl_write(sap->ann_fd, sap->ann, sap->ann_size);
diff --git a/libavutil/time.c b/libavutil/time.c
index 5a00e70..82f0abc 100644
--- a/libavutil/time.c
+++ b/libavutil/time.c
@@ -38,7 +38,15 @@
 
 int64_t av_gettime(void)
 {
-#if HAVE_GETTIMEOFDAY
+#if HAVE_CLOCK_GETTIME
+    /*
+     * POSIX.1-2008 marks gettimeofday() as obsolete,
+     * recommending the use of clock_gettime(2) instead.
+     */
+    struct timespec ts;
+    clock_gettime(CLOCK_REALTIME, &ts);
+    return (int64_t)ts.tv_sec * 1000000 + ts.tv_nsec / 1000;
+#elif HAVE_GETTIMEOFDAY
     struct timeval tv;
     gettimeofday(&tv, NULL);
     return (int64_t)tv.tv_sec * 1000000 + tv.tv_usec;
@@ -53,6 +61,17 @@ int64_t av_gettime(void)
 #endif
 }
 
+int64_t av_gettime_monotonic(void)
+{
+#if HAVE_CLOCK_GETTIME && defined(CLOCK_MONOTONIC)
+    struct timespec ts;
+    clock_gettime(CLOCK_MONOTONIC, &ts);
+    return (int64_t)ts.tv_sec * 1000000 + ts.tv_nsec / 1000;
+#else
+    return av_gettime();
+#endif
+}
+
 int av_usleep(unsigned usec)
 {
 #if HAVE_NANOSLEEP
@@ -68,3 +87,14 @@ int av_usleep(unsigned usec)
     return AVERROR(ENOSYS);
 #endif
 }
+
+int av_usleep_monotonic(int64_t usec)
+{
+#if HAVE_CLOCK_NANOSLEEP && defined(CLOCK_MONOTONIC)
+    struct timespec ts = { usec / 1000000, usec % 1000000 * 1000 };
+    while (clock_nanosleep(CLOCK_MONOTONIC, 0, &ts, &ts) < 0 && errno == EINTR);
+    return 0;
+#else
+    return av_usleep((unsigned)usec);
+#endif
+}
diff --git a/libavutil/time.h b/libavutil/time.h
index 90eb436..8e87717 100644
--- a/libavutil/time.h
+++ b/libavutil/time.h
@@ -29,6 +29,26 @@
 int64_t av_gettime(void);
 
 /**
+ * Get the current time in microseconds from the monotonic clock
+ * if available. If a monotonic clock  is not available on the
+ * targeted platform, the implementation fallsback on using
+ * av_gettime().
+ */
+int64_t av_gettime_monotonic(void);
+
+/**
+ * Sleep for a period of time by using the monotic clock.
+ * If a monotonic clock is not available on the targeted platform,
+ * the implementation fallsback on using av_usleep. Although the duration
+ * is expressed in microseconds, the actual delay may be rounded to the
+ * precision of the system timer.
+ *
+ * @param  usec Number of microseconds to sleep.
+ * @return zero on success or (negative) error code.
+ */
+int av_usleep_monotonic(int64_t usec);
+
+/**
  * Sleep for a period of time.  Although the duration is expressed in
  * microseconds, the actual delay may be rounded to the precision of the
  * system timer.
diff --git a/tools/aviocat.c b/tools/aviocat.c
index 56b918e..14619ab 100644
--- a/tools/aviocat.c
+++ b/tools/aviocat.c
@@ -82,7 +82,7 @@ int main(int argc, char **argv)
         goto fail;
     }
 
-    start_time = av_gettime();
+    start_time = av_gettime_monotonic();
     while (1) {
         uint8_t buf[1024];
         int n;
@@ -93,8 +93,8 @@ int main(int argc, char **argv)
         stream_pos += n;
         if (bps) {
             avio_flush(output);
-            while ((av_gettime() - start_time) * bps / AV_TIME_BASE < stream_pos)
-                av_usleep(50 * 1000);
+            while ((av_gettime_monotonic() - start_time) * bps / AV_TIME_BASE < stream_pos)
+                av_usleep_monotonic(50 * 1000);
         }
     }
 
-- 
1.9.2



More information about the ffmpeg-devel mailing list