Commit 6f2e3899 authored by Marton Balint's avatar Marton Balint

avformat/ftp: slightly rework file retrieval with seeking

- 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: 's avatarMarton Balint <cus@passwd.hu>
parent 04f1d497
...@@ -39,7 +39,8 @@ typedef enum { ...@@ -39,7 +39,8 @@ typedef enum {
DOWNLOADING, DOWNLOADING,
UPLOADING, UPLOADING,
LISTING_DIR, LISTING_DIR,
DISCONNECTED DISCONNECTED,
ENDOFFILE,
} FTPState; } FTPState;
typedef enum { typedef enum {
...@@ -231,7 +232,6 @@ static int ftp_send_command(FTPContext *s, const char *command, ...@@ -231,7 +232,6 @@ static int ftp_send_command(FTPContext *s, const char *command,
static void ftp_close_data_connection(FTPContext *s) static void ftp_close_data_connection(FTPContext *s)
{ {
ffurl_closep(&s->conn_data); ffurl_closep(&s->conn_data);
s->position = 0;
s->state = DISCONNECTED; s->state = DISCONNECTED;
} }
...@@ -740,8 +740,7 @@ static int ftp_open(URLContext *h, const char *url, int flags) ...@@ -740,8 +740,7 @@ static int ftp_open(URLContext *h, const char *url, int flags)
if (ftp_restart(s, 0) < 0) { if (ftp_restart(s, 0) < 0) {
h->is_streamed = 1; h->is_streamed = 1;
} else { } else {
if (ftp_file_size(s) < 0 && flags & AVIO_FLAG_READ) ftp_file_size(s);
h->is_streamed = 1;
if (s->write_seekable != 1 && flags & AVIO_FLAG_WRITE) if (s->write_seekable != 1 && flags & AVIO_FLAG_WRITE)
h->is_streamed = 1; h->is_streamed = 1;
} }
...@@ -758,7 +757,7 @@ static int64_t ftp_seek(URLContext *h, int64_t pos, int whence) ...@@ -758,7 +757,7 @@ static int64_t ftp_seek(URLContext *h, int64_t pos, int whence)
{ {
FTPContext *s = h->priv_data; FTPContext *s = h->priv_data;
int err; int err;
int64_t new_pos, fake_pos; int64_t new_pos;
ff_dlog(h, "ftp protocol seek %"PRId64" %d\n", pos, whence); 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) ...@@ -788,11 +787,10 @@ static int64_t ftp_seek(URLContext *h, int64_t pos, int whence)
return AVERROR(EINVAL); return AVERROR(EINVAL);
} }
fake_pos = s->filesize != -1 ? FFMIN(new_pos, s->filesize) : new_pos; if (new_pos != s->position) {
if (fake_pos != s->position) {
if ((err = ftp_abort(h)) < 0) if ((err = ftp_abort(h)) < 0)
return err; return err;
s->position = fake_pos; s->position = new_pos;
} }
return new_pos; return new_pos;
} }
...@@ -804,16 +802,13 @@ static int ftp_read(URLContext *h, unsigned char *buf, int size) ...@@ -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); ff_dlog(h, "ftp protocol read %d bytes\n", size);
retry: retry:
if (s->state == ENDOFFILE)
return AVERROR_EOF;
if (s->state == DISCONNECTED) { if (s->state == DISCONNECTED) {
/* optimization */
if (s->position >= s->filesize)
return AVERROR_EOF;
if ((err = ftp_connect_data_connection(h)) < 0) if ((err = ftp_connect_data_connection(h)) < 0)
return err; return err;
} }
if (s->state == READY) { if (s->state == READY) {
if (s->position >= s->filesize)
return AVERROR_EOF;
if ((err = ftp_retrieve(s)) < 0) if ((err = ftp_retrieve(s)) < 0)
return err; return err;
} }
...@@ -821,27 +816,28 @@ static int ftp_read(URLContext *h, unsigned char *buf, int size) ...@@ -821,27 +816,28 @@ static int ftp_read(URLContext *h, unsigned char *buf, int size)
read = ffurl_read(s->conn_data, buf, size); read = ffurl_read(s->conn_data, buf, size);
if (read >= 0) { if (read >= 0) {
s->position += read; s->position += read;
if (s->position >= s->filesize) { s->filesize = FFMAX(s->filesize, s->position);
/* server will terminate, but keep current position to avoid madness */ }
/* save position to restart from it */ if (read == AVERROR_EOF) {
int64_t pos = s->position; static const int retr_codes[] = {226, 250, 425, 426, 451, 0};
if (ftp_abort(h) < 0) { char *response = NULL;
s->position = pos; err = ftp_status(s, &response, retr_codes);
return AVERROR(EIO); if (err == 226) {
} ftp_close_data_connection(s);
s->position = pos; 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 */ /* Server closed connection. Probably due to inactivity */
int64_t pos = s->position;
av_log(h, AV_LOG_INFO, "Reconnect to FTP server.\n"); av_log(h, AV_LOG_INFO, "Reconnect to FTP server.\n");
if ((err = ftp_abort(h)) < 0) if ((err = ftp_abort(h)) < 0)
return err; 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) { if (!retry_done) {
retry_done = 1; retry_done = 1;
goto retry; goto retry;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment