[FFmpeg-devel] avformat/http: fix http reconnect

Submitted by 卢炯健 on March 14, 2017, 10:50 a.m.

Details

Message ID 20170314105024.20051-1-lujiongjian1005@gmail.com
State New
Headers show

Commit Message

卢炯健 March 14, 2017, 10:50 a.m.
I use the commad line below:
ffplay -reconnect 1 -reconnect_at_eof 1 -reconnect_streamed 1 -reconnect_delay_max 4000 http://movie_url.mp4
I find that if network source down, ffplay keep reconnect only a few seconds even if I set -reconnect_delay_max 4000. So I reopen http connection cyclically until timeout, reconnect successfully or user quit.
My patch's commad line is as below:
ffplay -reconnect 1 -reconnect_at_eof 1 -reconnect_streamed 1 -reconnect_timeout 30 http://movie_url.mp4

Signed-off-by: Wing Lo <lujiongjian1005@gmail.com>
---
 libavformat/http.c | 56 +++++++++++++++++++++++++++++++++++++-----------------
 1 file changed, 39 insertions(+), 17 deletions(-)

Patch hide | download patch | download mbox

diff --git a/libavformat/http.c b/libavformat/http.c
index 293a8a7..d38c569 100644
--- a/libavformat/http.c
+++ b/libavformat/http.c
@@ -112,8 +112,8 @@  typedef struct HTTPContext {
     int reconnect;
     int reconnect_at_eof;
     int reconnect_streamed;
-    int reconnect_delay;
-    int reconnect_delay_max;
+    int reconnect_timeout;
+    uint8_t can_reconnect;
     int listen;
     char *resource;
     int reply_code;
@@ -156,7 +156,7 @@  static const AVOption options[] = {
     { "reconnect", "auto reconnect after disconnect before EOF", OFFSET(reconnect), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, D },
     { "reconnect_at_eof", "auto reconnect at EOF", OFFSET(reconnect_at_eof), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, D },
     { "reconnect_streamed", "auto reconnect streamed / non seekable streams", OFFSET(reconnect_streamed), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, D },
-    { "reconnect_delay_max", "max reconnect delay in seconds after which to give up", OFFSET(reconnect_delay_max), AV_OPT_TYPE_INT, { .i64 = 120 }, 0, UINT_MAX/1000/1000, D },
+	{ "reconnect_timeout", "set timeout (in seconds) for http reconnection", OFFSET(reconnect_timeout), AV_OPT_TYPE_INT, { .i64 = 120 }, 0, UINT_MAX/1000/1000, D },
     { "listen", "listen on HTTP", OFFSET(listen), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 2, D | E },
     { "resource", "The resource requested by a client", OFFSET(resource), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, E },
     { "reply_code", "The http status code to return to a client", OFFSET(reply_code), AV_OPT_TYPE_INT, { .i64 = 200}, INT_MIN, 599, E},
@@ -489,6 +489,7 @@  static int http_open(URLContext *h, const char *uri, int flags,
     else
         h->is_streamed = 1;
 
+    s->can_reconnect = 1;
     s->filesize = UINT64_MAX;
     s->location = av_strdup(uri);
     if (!s->location)
@@ -1270,11 +1271,42 @@  static int http_buf_read_compressed(URLContext *h, uint8_t *buf, int size)
 
 static int64_t http_seek_internal(URLContext *h, int64_t off, int whence, int force_reconnect);
 
+static int http_reconnect(URLContext *h)
+{
+    int64_t wait_start = 0;
+    int64_t seek_ret;
+    HTTPContext *s = h->priv_data;
+    uint64_t target = h->is_streamed ? 0 : s->off;
+    int timeout = s->reconnect_timeout * 1000*1000;
+
+    if (!s->can_reconnect)
+        return -1;
+
+    av_log(h, AV_LOG_INFO, "Will reconnect at %"PRIu64".\n", s->off);
+    do {
+        if (ff_check_interrupt(&h->interrupt_callback))
+            return AVERROR_EXIT;
+        seek_ret = http_seek_internal(h, target, SEEK_SET, 1);
+        usleep(1000*1000);
+        if (timeout > 0) {
+            if (!wait_start)
+                wait_start = av_gettime_relative();
+            else if (av_gettime_relative() - wait_start > timeout) {
+                av_log(h, AV_LOG_ERROR, "Reconnect time out. Failed to reconnect at %"PRIu64".\n", target);
+                s->can_reconnect = 0;
+                return AVERROR(ETIMEDOUT);
+            }
+        }
+    }while (seek_ret != target);
+
+    av_log(h, AV_LOG_INFO, "Successful to reconnect at %"PRIu64".\n", target);
+    return 0;
+}
+
 static int http_read_stream(URLContext *h, uint8_t *buf, int size)
 {
     HTTPContext *s = h->priv_data;
     int err, new_location, read_ret;
-    int64_t seek_ret;
 
     if (!s->hd)
         return AVERROR_EOF;
@@ -1292,23 +1324,13 @@  static int http_read_stream(URLContext *h, uint8_t *buf, int size)
     read_ret = http_buf_read(h, buf, size);
     if (   (read_ret  < 0 && s->reconnect        && (!h->is_streamed || s->reconnect_streamed) && s->filesize > 0 && s->off < s->filesize)
         || (read_ret == 0 && s->reconnect_at_eof && (!h->is_streamed || s->reconnect_streamed))) {
-        uint64_t target = h->is_streamed ? 0 : s->off;
+        av_log(h, AV_LOG_INFO, "http buffer read error=%s.\n", av_err2str(read_ret));
 
-        if (s->reconnect_delay > s->reconnect_delay_max)
-            return AVERROR(EIO);
-
-        av_log(h, AV_LOG_INFO, "Will reconnect at %"PRIu64" error=%s.\n", s->off, av_err2str(read_ret));
-        av_usleep(1000U*1000*s->reconnect_delay);
-        s->reconnect_delay = 1 + 2*s->reconnect_delay;
-        seek_ret = http_seek_internal(h, target, SEEK_SET, 1);
-        if (seek_ret != target) {
-            av_log(h, AV_LOG_ERROR, "Failed to reconnect at %"PRIu64".\n", target);
+        if (http_reconnect(h) < 0)
             return read_ret;
-        }
 
         read_ret = http_buf_read(h, buf, size);
-    } else
-        s->reconnect_delay = 0;
+    }
 
     return read_ret;
 }