diff mbox series

[FFmpeg-devel,12/12] avformat/ftp: slightly rework file retrieval with seeking

Message ID 20200208211823.31345-12-cus@passwd.hu
State Accepted
Headers show
Series [FFmpeg-devel,01/12] avformat/tests/url: make format more readable
Related show

Checks

Context Check Description
andriy/ffmpeg-patchwork pending
andriy/ffmpeg-patchwork success Applied patch
andriy/ffmpeg-patchwork success Configure finished
andriy/ffmpeg-patchwork success Make finished
andriy/ffmpeg-patchwork success Make fate finished

Commit Message

Marton Balint Feb. 8, 2020, 9:18 p.m. UTC
- do not require a known file size for seek to work
- read the files till the actual end, do not limit data at the queried file
  size
- fix a bug which causes reading 0 byte files for non-existing files
- properly check the return status of the FTP server at the end of the file
  retrieval

Signed-off-by: Marton Balint <cus@passwd.hu>
---
 libavformat/ftp.c | 54 +++++++++++++++++++++++++-----------------------------
 1 file changed, 25 insertions(+), 29 deletions(-)
diff mbox series

Patch

diff --git a/libavformat/ftp.c b/libavformat/ftp.c
index ab7368256c..e3d194da58 100644
--- a/libavformat/ftp.c
+++ b/libavformat/ftp.c
@@ -39,7 +39,8 @@  typedef enum {
     DOWNLOADING,
     UPLOADING,
     LISTING_DIR,
-    DISCONNECTED
+    DISCONNECTED,
+    ENDOFFILE,
 } FTPState;
 
 typedef enum {
@@ -231,7 +232,6 @@  static int ftp_send_command(FTPContext *s, const char *command,
 static void ftp_close_data_connection(FTPContext *s)
 {
     ffurl_closep(&s->conn_data);
-    s->position = 0;
     s->state = DISCONNECTED;
 }
 
@@ -740,8 +740,7 @@  static int ftp_open(URLContext *h, const char *url, int flags)
     if (ftp_restart(s, 0) < 0) {
         h->is_streamed = 1;
     } else {
-        if (ftp_file_size(s) < 0 && flags & AVIO_FLAG_READ)
-            h->is_streamed = 1;
+        ftp_file_size(s);
         if (s->write_seekable != 1 && flags & AVIO_FLAG_WRITE)
             h->is_streamed = 1;
     }
@@ -758,7 +757,7 @@  static int64_t ftp_seek(URLContext *h, int64_t pos, int whence)
 {
     FTPContext *s = h->priv_data;
     int err;
-    int64_t new_pos, fake_pos;
+    int64_t new_pos;
 
     ff_dlog(h, "ftp protocol seek %"PRId64" %d\n", pos, whence);
 
@@ -788,11 +787,10 @@  static int64_t ftp_seek(URLContext *h, int64_t pos, int whence)
         return AVERROR(EINVAL);
     }
 
-    fake_pos = s->filesize != -1 ? FFMIN(new_pos, s->filesize) : new_pos;
-    if (fake_pos != s->position) {
+    if (new_pos != s->position) {
         if ((err = ftp_abort(h)) < 0)
             return err;
-        s->position = fake_pos;
+        s->position = new_pos;
     }
     return new_pos;
 }
@@ -804,16 +802,13 @@  static int ftp_read(URLContext *h, unsigned char *buf, int size)
 
     ff_dlog(h, "ftp protocol read %d bytes\n", size);
   retry:
+    if (s->state == ENDOFFILE)
+        return AVERROR_EOF;
     if (s->state == DISCONNECTED) {
-        /* optimization */
-        if (s->position >= s->filesize)
-            return AVERROR_EOF;
         if ((err = ftp_connect_data_connection(h)) < 0)
             return err;
     }
     if (s->state == READY) {
-        if (s->position >= s->filesize)
-            return AVERROR_EOF;
         if ((err = ftp_retrieve(s)) < 0)
             return err;
     }
@@ -821,27 +816,28 @@  static int ftp_read(URLContext *h, unsigned char *buf, int size)
         read = ffurl_read(s->conn_data, buf, size);
         if (read >= 0) {
             s->position += read;
-            if (s->position >= s->filesize) {
-                /* server will terminate, but keep current position to avoid madness */
-                /* save position to restart from it */
-                int64_t pos = s->position;
-                if (ftp_abort(h) < 0) {
-                    s->position = pos;
-                    return AVERROR(EIO);
-                }
-                s->position = pos;
-            }
+            s->filesize = FFMAX(s->filesize, s->position);
+        }
+        if (read == AVERROR_EOF) {
+           static const int retr_codes[] = {226, 250, 425, 426, 451, 0};
+           char *response = NULL;
+           err = ftp_status(s, &response, retr_codes);
+           if (err == 226) {
+               ftp_close_data_connection(s);
+               av_freep(&response);
+               s->state = ENDOFFILE;
+               return AVERROR_EOF;
+           }
+           /* 250 is not allowed, any other status means some kind of error */
+           av_log(h, AV_LOG_ERROR, "FTP transfer failed: %s\n", response ? response : (err < 0 ? av_err2str(err) : "?"));
+           av_freep(&response);
+           read = AVERROR(EIO);
         }
-        if (read <= 0 && s->position < s->filesize && !h->is_streamed) {
+        if (read <= 0 && !h->is_streamed) {
             /* Server closed connection. Probably due to inactivity */
-            int64_t pos = s->position;
             av_log(h, AV_LOG_INFO, "Reconnect to FTP server.\n");
             if ((err = ftp_abort(h)) < 0)
                 return err;
-            if ((err = ftp_seek(h, pos, SEEK_SET)) < 0) {
-                av_log(h, AV_LOG_ERROR, "Position cannot be restored.\n");
-                return err;
-            }
             if (!retry_done) {
                 retry_done = 1;
                 goto retry;