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

Olivier Langlois olivier at trillion01.com
Fri May 2 00:18:46 CEST 2014


Also add similar av_usleep_relative().

This is third version of the patch that accounts for Nicolas George comments.

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       | 14 ++------------
 libavformat/avio.c       |  4 ++--
 libavformat/network.c    |  4 ++--
 libavformat/sapenc.c     |  2 +-
 libavutil/time.c         | 41 ++++++++++++++++++++++++++++++++++++++++-
 libavutil/time.h         | 28 ++++++++++++++++++++++++++++
 tools/aviocat.c          |  6 +++---
 14 files changed, 108 insertions(+), 49 deletions(-)

diff --git a/cmdutils_opencl.c b/cmdutils_opencl.c
index 2a04db9..0fa8cc0 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_relative();
     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_relative() - start)/OPENCL_NB_ITER;
 end:
     if (kernel)
         clReleaseKernel(kernel);
diff --git a/configure b/configure
index 261fedb..7a193db 100755
--- a/configure
+++ b/configure
@@ -1659,6 +1659,7 @@ SYSTEM_FUNCS="
     access
     aligned_malloc
     clock_gettime
+    clock_nanosleep
     closesocket
     CommandLineToArgvW
     CoTaskMemFree
@@ -4479,6 +4480,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 b2e39e8..2a4c000 100644
--- a/ffmpeg.c
+++ b/ffmpeg.c
@@ -2382,7 +2382,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_relative();
     }
 
     /* output stream init */
@@ -3175,7 +3175,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_relative() - ist->start;
             if (pts > now)
                 return AVERROR(EAGAIN);
         }
@@ -3541,7 +3541,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_relative();
 
 #if HAVE_PTHREADS
     if ((ret = init_input_threads()) < 0)
@@ -3549,7 +3549,7 @@ static int transcode(void)
 #endif
 
     while (!received_sigterm) {
-        int64_t cur_time= av_gettime();
+        int64_t cur_time= av_gettime_relative();
 
         /* if 'q' pressed, exits */
         if (stdin_interaction)
@@ -3596,7 +3596,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_relative());
 
     /* close each encoder */
     for (i = 0; i < nb_output_streams; i++) {
diff --git a/ffplay.c b/ffplay.c
index 86b9126..54b1ca8 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_relative() - 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_relative() / 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_relative() / 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_relative() / 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_relative() / 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_relative() / 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_relative()/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_relative();
         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_relative() / 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_relative() / 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_relative();
 
     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_relative() - 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_relative();
             if (event.type == SDL_MOUSEBUTTONDOWN) {
                 x = event.button.x;
             } else {
diff --git a/libavcodec/dct-test.c b/libavcodec/dct-test.c
index 6308348..2f4c3f7 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_relative();
     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_relative() - 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_relative();
     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_relative() - 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..7d8f8e5 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_relative();
             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_relative() - time_start;
             if (duration >= 1000000)
                 break;
             nb_its *= 2;
diff --git a/libavcodec/motion-test.c b/libavcodec/motion-test.c
index 53cfedb..3861601 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_relative();
     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_relative() - 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 4b3f6a8..cbc70e6 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;
@@ -448,7 +438,7 @@ static int init_convert_timestamp(AVFormatContext *ctx, int64_t ts)
     }
 #if HAVE_CLOCK_GETTIME && defined(CLOCK_MONOTONIC)
     if (ctx->streams[0]->avg_frame_rate.num) {
-        now = av_gettime_monotonic();
+        now = av_gettime_relative();
         if (s->ts_mode == V4L_TS_MONO2ABS ||
             (ts <= now + 1 * AV_TIME_BASE && ts >= now - 10 * AV_TIME_BASE)) {
             AVRational tb = {AV_TIME_BASE, 1};
@@ -479,7 +469,7 @@ static int convert_timestamp(AVFormatContext *ctx, int64_t *ts)
 #if HAVE_CLOCK_GETTIME && defined(CLOCK_MONOTONIC)
     if (s->timefilter) {
         int64_t nowa = av_gettime();
-        int64_t nowm = av_gettime_monotonic();
+        int64_t nowm = av_gettime_relative();
         ff_timefilter_update(s->timefilter, nowa, nowm - s->last_time_m);
         s->last_time_m = nowm;
         *ts = ff_timefilter_eval(s->timefilter, *ts - nowm);
diff --git a/libavformat/avio.c b/libavformat/avio.c
index 4edaaa6..0a2a0a9 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_relative();
+                    else if (av_gettime_relative() > wait_since + h->rw_timeout)
                         return AVERROR(EIO);
                 }
                 av_usleep(1000);
diff --git a/libavformat/network.c b/libavformat/network.c
index 5e574e3..9f02ec6 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_relative();
+            else if (av_gettime_relative() - wait_start > timeout)
                 return AVERROR(ETIMEDOUT);
         }
     }
diff --git a/libavformat/sapenc.c b/libavformat/sapenc.c
index 738e8b8..2564698 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_relative();
 
     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..8be479a 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,26 @@ int64_t av_gettime(void)
 #endif
 }
 
+int64_t av_gettime_relative(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_gettime_relative_is_monotonic(void)
+{
+#if HAVE_CLOCK_GETTIME && defined(CLOCK_MONOTONIC)
+    return 1;
+#else
+    return 0;
+#endif
+}
+
 int av_usleep(unsigned usec)
 {
 #if HAVE_NANOSLEEP
@@ -68,3 +96,14 @@ int av_usleep(unsigned usec)
     return AVERROR(ENOSYS);
 #endif
 }
+
+int av_usleep_relative(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..ec97fbc 100644
--- a/libavutil/time.h
+++ b/libavutil/time.h
@@ -29,6 +29,34 @@
 int64_t av_gettime(void);
 
 /**
+ * Get the current time in microseconds since some unspecified starting point.
+ * On platforms that support it, the time comes from a monotonic clock
+ * This property makes this time source ideal for measuring relative time.
+ * If a monotonic clock is not available on the targeted platform, the
+ * implementation fallsback on using av_gettime().
+ */
+int64_t av_gettime_relative(void);
+
+/**
+ * Indicates with a boolean result if the av_gettime_relative() time source
+ * is monotonic.
+ */
+int av_gettime_relative_is_monotonic(void);
+
+/**
+ * Sleep for a period of time by using the relative time source.
+ * On platforms that support it, the sleep is measured with a monotonic 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_relative(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..2a8c2ca 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_relative();
     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_relative() - start_time) * bps / AV_TIME_BASE < stream_pos)
+                av_usleep_relative(50 * 1000);
         }
     }
 
-- 
1.9.2



More information about the ffmpeg-devel mailing list