ftp: reconnect on read
authorLukasz Marek <lukasz.m.luki@gmail.com>
Mon, 27 May 2013 18:53:27 +0000 (20:53 +0200)
committerLukasz Marek <lukasz.m.luki@gmail.com>
Fri, 31 May 2013 14:40:03 +0000 (16:40 +0200)
FTP server may disconnect after some period of time when no transfer is done.

FTP protocol should reconnect and seek to last position.

libavformat/ftp.c

index da9408a..e497c1a 100644 (file)
@@ -38,7 +38,6 @@ typedef enum {
     DISCONNECTED
 } FTPState;
 
-
 typedef struct {
     const AVClass *class;
     URLContext *conn_control;                    /**< Control connection */
@@ -499,57 +498,6 @@ static int ftp_open(URLContext *h, const char *url, int flags)
     return err;
 }
 
-static int ftp_read(URLContext *h, unsigned char *buf, int size)
-{
-    FTPContext *s = h->priv_data;
-    int read;
-
-    av_dlog(h, "ftp protocol read %d bytes\n", size);
-
-    if (s->state == READY) {
-        ftp_retrieve(s);
-    }
-    if (s->conn_data && s->state == DOWNLOADING) {
-        read = ffurl_read(s->conn_data, buf, size);
-        if (read >= 0) {
-            s->position += read;
-            if (s->position >= s->filesize) {
-                ffurl_closep(&s->conn_data);
-                s->state = DISCONNECTED;
-                if (ftp_status(s, NULL, NULL, NULL,NULL, 226) != 226)
-                    return AVERROR(EIO);
-            }
-        }
-        return read;
-    }
-
-    av_log(h, AV_LOG_DEBUG, "FTP read failed\n");
-    return AVERROR(EIO);
-}
-
-static int ftp_write(URLContext *h, const unsigned char *buf, int size)
-{
-    FTPContext *s = h->priv_data;
-    int written;
-
-    av_dlog(h, "ftp protocol write %d bytes\n", size);
-
-    if (s->state == READY) {
-        ftp_store(s);
-    }
-    if (s->conn_data && s->state == UPLOADING) {
-        written = ffurl_write(s->conn_data, buf, size);
-        if (written > 0) {
-            s->position += written;
-            s->filesize = FFMAX(s->filesize, s->position);
-        }
-        return written;
-    }
-
-    av_log(h, AV_LOG_ERROR, "FTP write failed\n");
-    return AVERROR(EIO);
-}
-
 static int64_t ftp_seek(URLContext *h, int64_t pos, int whence)
 {
     FTPContext *s = h->priv_data;
@@ -618,6 +566,79 @@ static int64_t ftp_seek(URLContext *h, int64_t pos, int whence)
     return new_pos;
 }
 
+static int ftp_read(URLContext *h, unsigned char *buf, int size)
+{
+    FTPContext *s = h->priv_data;
+    int read, err, retry_done = 0;
+
+    av_dlog(h, "ftp protocol read %d bytes\n", size);
+  retry:
+    if (s->state == READY) {
+        ftp_retrieve(s);
+    }
+    if (s->conn_data && s->state == DOWNLOADING) {
+        read = ffurl_read(s->conn_data, buf, size);
+        if (read >= 0) {
+            s->position += read;
+            if (s->position >= s->filesize) {
+                ffurl_closep(&s->conn_data);
+                s->state = DISCONNECTED;
+                if (ftp_status(s, NULL, NULL, NULL,NULL, 226) != 226)
+                    return AVERROR(EIO);
+            }
+        }
+        if (!read && s->position < s->filesize && !h->is_streamed) {
+            /* Server closed connection. Probably due to inactivity */
+            /* TODO: Consider retry before reconnect */
+            int64_t pos = s->position;
+            av_log(h, AV_LOG_INFO, "Reconnect to FTP server.\n");
+            ffurl_closep(&s->conn_control);
+            ffurl_closep(&s->conn_data);
+            s->position = 0;
+            s->state = DISCONNECTED;
+            if ((err = ftp_connect_control_connection(h)) < 0) {
+                av_log(h, AV_LOG_ERROR, "Reconnect failed\n");
+                return err;
+            }
+            if ((err = ftp_seek(h, pos, SEEK_SET)) < 0) {
+                av_dlog(h, "Seek failed after reconnect\n");
+                return err;
+            }
+            if (!retry_done) {
+                retry_done = 1;
+                goto retry;
+            }
+        }
+        return read;
+    }
+
+    av_log(h, AV_LOG_DEBUG, "FTP read failed\n");
+    return AVERROR(EIO);
+}
+
+static int ftp_write(URLContext *h, const unsigned char *buf, int size)
+{
+    FTPContext *s = h->priv_data;
+    int written;
+
+    av_dlog(h, "ftp protocol write %d bytes\n", size);
+
+    if (s->state == READY) {
+        ftp_store(s);
+    }
+    if (s->conn_data && s->state == UPLOADING) {
+        written = ffurl_write(s->conn_data, buf, size);
+        if (written > 0) {
+            s->position += written;
+            s->filesize = FFMAX(s->filesize, s->position);
+        }
+        return written;
+    }
+
+    av_log(h, AV_LOG_ERROR, "FTP write failed\n");
+    return AVERROR(EIO);
+}
+
 static int ftp_close(URLContext *h)
 {
     FTPContext *s = h->priv_data;