[FFmpeg-cvslog] Merge commit 'fab8156b2f30666adabe227b3d7712fd193873b1'
Derek Buitenhuis
git at videolan.org
Thu Apr 21 16:55:28 CEST 2016
ffmpeg | branch: master | Derek Buitenhuis <derek.buitenhuis at gmail.com> | Thu Apr 21 15:55:09 2016 +0100| [f8e89d8a297e034d03aac88c73acd1541167ae5e] | committer: Derek Buitenhuis
Merge commit 'fab8156b2f30666adabe227b3d7712fd193873b1'
* commit 'fab8156b2f30666adabe227b3d7712fd193873b1':
avio: Copy URLContext generic options into child URLContexts
Merged-by: Derek Buitenhuis <derek.buitenhuis at gmail.com>
> http://git.videolan.org/gitweb.cgi/ffmpeg.git/?a=commit;h=f8e89d8a297e034d03aac88c73acd1541167ae5e
---
libavformat/async.c | 2 +-
libavformat/avio.c | 7 +++++--
libavformat/aviobuf.c | 2 +-
libavformat/cache.c | 2 +-
libavformat/concat.c | 2 +-
libavformat/crypto.c | 2 +-
libavformat/ftp.c | 4 ++--
libavformat/gopher.c | 2 +-
libavformat/hlsproto.c | 2 +-
libavformat/http.c | 6 +++---
libavformat/icecast.c | 2 +-
libavformat/md5proto.c | 2 +-
libavformat/mmst.c | 2 +-
libavformat/rtmpcrypt.c | 2 +-
libavformat/rtmpproto.c | 4 ++--
libavformat/rtpproto.c | 6 +++---
libavformat/rtsp.c | 10 +++++-----
libavformat/rtspdec.c | 4 ++--
libavformat/sapdec.c | 2 +-
libavformat/sapenc.c | 4 ++--
libavformat/smoothstreamingenc.c | 6 +++---
libavformat/srtpproto.c | 2 +-
libavformat/subfile.c | 2 +-
libavformat/tls.c | 2 +-
libavformat/url.h | 5 ++++-
25 files changed, 46 insertions(+), 40 deletions(-)
diff --git a/libavformat/async.c b/libavformat/async.c
index 0cc6fb0..54dbd23 100644
--- a/libavformat/async.c
+++ b/libavformat/async.c
@@ -251,7 +251,7 @@ static int async_open(URLContext *h, const char *arg, int flags, AVDictionary **
/* wrap interrupt callback */
c->interrupt_callback = h->interrupt_callback;
- ret = ffurl_open_whitelist(&c->inner, arg, flags, &interrupt_callback, options, h->protocol_whitelist, h->protocol_blacklist);
+ ret = ffurl_open_whitelist(&c->inner, arg, flags, &interrupt_callback, options, h->protocol_whitelist, h->protocol_blacklist, h);
if (ret != 0) {
av_log(h, AV_LOG_ERROR, "ffurl_open failed : %s, %s\n", av_err2str(ret), arg);
goto url_fail;
diff --git a/libavformat/avio.c b/libavformat/avio.c
index 7e68c9a..6507593 100644
--- a/libavformat/avio.c
+++ b/libavformat/avio.c
@@ -305,13 +305,16 @@ int ffurl_alloc(URLContext **puc, const char *filename, int flags,
int ffurl_open_whitelist(URLContext **puc, const char *filename, int flags,
const AVIOInterruptCB *int_cb, AVDictionary **options,
- const char *whitelist, const char* blacklist)
+ const char *whitelist, const char* blacklist,
+ URLContext *parent)
{
AVDictionary *tmp_opts = NULL;
AVDictionaryEntry *e;
int ret = ffurl_alloc(puc, filename, flags, int_cb);
if (ret < 0)
return ret;
+ if (parent)
+ av_opt_copy(*puc, parent);
if (options &&
(ret = av_opt_set_dict(*puc, options)) < 0)
goto fail;
@@ -352,7 +355,7 @@ int ffurl_open(URLContext **puc, const char *filename, int flags,
const AVIOInterruptCB *int_cb, AVDictionary **options)
{
return ffurl_open_whitelist(puc, filename, flags,
- int_cb, options, NULL, NULL);
+ int_cb, options, NULL, NULL, NULL);
}
static inline int retry_transfer_wrapper(URLContext *h, uint8_t *buf,
diff --git a/libavformat/aviobuf.c b/libavformat/aviobuf.c
index f2f03f6..f02ae21 100644
--- a/libavformat/aviobuf.c
+++ b/libavformat/aviobuf.c
@@ -989,7 +989,7 @@ int ffio_open_whitelist(AVIOContext **s, const char *filename, int flags,
URLContext *h;
int err;
- err = ffurl_open_whitelist(&h, filename, flags, int_cb, options, whitelist, blacklist);
+ err = ffurl_open_whitelist(&h, filename, flags, int_cb, options, whitelist, blacklist, NULL);
if (err < 0)
return err;
err = ffio_fdopen(s, h);
diff --git a/libavformat/cache.c b/libavformat/cache.c
index 25b28d5..6aabca2 100644
--- a/libavformat/cache.c
+++ b/libavformat/cache.c
@@ -87,7 +87,7 @@ static int cache_open(URLContext *h, const char *arg, int flags, AVDictionary **
av_freep(&buffername);
return ffurl_open_whitelist(&c->inner, arg, flags, &h->interrupt_callback,
- options, h->protocol_whitelist, h->protocol_blacklist);
+ options, h->protocol_whitelist, h->protocol_blacklist, h);
}
static int add_entry(URLContext *h, const unsigned char *buf, int size)
diff --git a/libavformat/concat.c b/libavformat/concat.c
index 288bf14..46b520f 100644
--- a/libavformat/concat.c
+++ b/libavformat/concat.c
@@ -98,7 +98,7 @@ static av_cold int concat_open(URLContext *h, const char *uri, int flags)
/* creating URLContext */
err = ffurl_open_whitelist(&uc, node_uri, flags,
- &h->interrupt_callback, NULL, h->protocol_whitelist, h->protocol_blacklist);
+ &h->interrupt_callback, NULL, h->protocol_whitelist, h->protocol_blacklist, h);
if (err < 0)
break;
diff --git a/libavformat/crypto.c b/libavformat/crypto.c
index 4222e1e..2999f50 100644
--- a/libavformat/crypto.c
+++ b/libavformat/crypto.c
@@ -138,7 +138,7 @@ static int crypto_open2(URLContext *h, const char *uri, int flags, AVDictionary
if ((ret = ffurl_open_whitelist(&c->hd, nested_url, flags,
&h->interrupt_callback, options,
- h->protocol_whitelist, h->protocol_blacklist)) < 0) {
+ h->protocol_whitelist, h->protocol_blacklist, h)) < 0) {
av_log(h, AV_LOG_ERROR, "Unable to open resource: %s\n", nested_url);
goto err;
}
diff --git a/libavformat/ftp.c b/libavformat/ftp.c
index b9fee24..0663b47 100644
--- a/libavformat/ftp.c
+++ b/libavformat/ftp.c
@@ -543,7 +543,7 @@ static int ftp_connect_control_connection(URLContext *h)
} /* if option is not given, don't pass it and let tcp use its own default */
err = ffurl_open_whitelist(&s->conn_control, buf, AVIO_FLAG_READ_WRITE,
&h->interrupt_callback, &opts,
- h->protocol_whitelist, h->protocol_blacklist);
+ h->protocol_whitelist, h->protocol_blacklist, h);
av_dict_free(&opts);
if (err < 0) {
av_log(h, AV_LOG_ERROR, "Cannot open control connection\n");
@@ -597,7 +597,7 @@ static int ftp_connect_data_connection(URLContext *h)
} /* if option is not given, don't pass it and let tcp use its own default */
err = ffurl_open_whitelist(&s->conn_data, buf, h->flags,
&h->interrupt_callback, &opts,
- h->protocol_whitelist, h->protocol_blacklist);
+ h->protocol_whitelist, h->protocol_blacklist, h);
av_dict_free(&opts);
if (err < 0)
return err;
diff --git a/libavformat/gopher.c b/libavformat/gopher.c
index d1113e7..3070b24 100644
--- a/libavformat/gopher.c
+++ b/libavformat/gopher.c
@@ -94,7 +94,7 @@ static int gopher_open(URLContext *h, const char *uri, int flags)
s->hd = NULL;
err = ffurl_open_whitelist(&s->hd, buf, AVIO_FLAG_READ_WRITE,
- &h->interrupt_callback, NULL, h->protocol_whitelist, h->protocol_blacklist);
+ &h->interrupt_callback, NULL, h->protocol_whitelist, h->protocol_blacklist, h);
if (err < 0)
goto fail;
diff --git a/libavformat/hlsproto.c b/libavformat/hlsproto.c
index 097e520..2b19ed0 100644
--- a/libavformat/hlsproto.c
+++ b/libavformat/hlsproto.c
@@ -307,7 +307,7 @@ retry:
av_log(h, AV_LOG_DEBUG, "opening %s\n", url);
ret = ffurl_open_whitelist(&s->seg_hd, url, AVIO_FLAG_READ,
&h->interrupt_callback, NULL,
- h->protocol_whitelist, h->protocol_blacklist);
+ h->protocol_whitelist, h->protocol_blacklist, h);
if (ret < 0) {
if (ff_check_interrupt(&h->interrupt_callback))
return AVERROR_EXIT;
diff --git a/libavformat/http.c b/libavformat/http.c
index 814ca01..622814b 100644
--- a/libavformat/http.c
+++ b/libavformat/http.c
@@ -221,7 +221,7 @@ static int http_open_cnx_internal(URLContext *h, AVDictionary **options)
if (!s->hd) {
err = ffurl_open_whitelist(&s->hd, buf, AVIO_FLAG_READ_WRITE,
&h->interrupt_callback, options,
- h->protocol_whitelist, h->protocol_blacklist);
+ h->protocol_whitelist, h->protocol_blacklist, h);
if (err < 0)
return err;
}
@@ -456,7 +456,7 @@ static int http_listen(URLContext *h, const char *uri, int flags,
goto fail;
if ((ret = ffurl_open_whitelist(&s->hd, lower_url, AVIO_FLAG_READ_WRITE,
&h->interrupt_callback, options,
- h->protocol_whitelist, h->protocol_blacklist
+ h->protocol_whitelist, h->protocol_blacklist, h
)) < 0)
goto fail;
s->handshake_step = LOWER_PROTO;
@@ -1582,7 +1582,7 @@ static int http_proxy_open(URLContext *h, const char *uri, int flags)
redo:
ret = ffurl_open_whitelist(&s->hd, lower_url, AVIO_FLAG_READ_WRITE,
&h->interrupt_callback, NULL,
- h->protocol_whitelist, h->protocol_blacklist);
+ h->protocol_whitelist, h->protocol_blacklist, h);
if (ret < 0)
return ret;
diff --git a/libavformat/icecast.c b/libavformat/icecast.c
index f7ca5cb..02e3e38 100644
--- a/libavformat/icecast.c
+++ b/libavformat/icecast.c
@@ -165,7 +165,7 @@ static int icecast_open(URLContext *h, const char *uri, int flags)
ff_url_join(h_url, sizeof(h_url), "http", auth, host, port, "%s", path);
// Finally open http proto handler
ret = ffurl_open_whitelist(&s->hd, h_url, AVIO_FLAG_READ_WRITE, NULL,
- &opt_dict, h->protocol_whitelist, h->protocol_blacklist);
+ &opt_dict, h->protocol_whitelist, h->protocol_blacklist, h);
cleanup:
av_freep(&user);
diff --git a/libavformat/md5proto.c b/libavformat/md5proto.c
index 2df34c6..0e04b90 100644
--- a/libavformat/md5proto.c
+++ b/libavformat/md5proto.c
@@ -71,7 +71,7 @@ static int md5_close(URLContext *h)
if (*filename) {
err = ffurl_open_whitelist(&out, filename, AVIO_FLAG_WRITE,
&h->interrupt_callback, NULL,
- h->protocol_whitelist, h->protocol_blacklist);
+ h->protocol_whitelist, h->protocol_blacklist, h);
if (err)
return err;
err = ffurl_write(out, buf, i*2+1);
diff --git a/libavformat/mmst.c b/libavformat/mmst.c
index 0f48b46..1d13959 100644
--- a/libavformat/mmst.c
+++ b/libavformat/mmst.c
@@ -530,7 +530,7 @@ static int mms_open(URLContext *h, const char *uri, int flags)
ff_url_join(tcpname, sizeof(tcpname), "tcp", NULL, mmst->host, port, NULL);
err = ffurl_open_whitelist(&mms->mms_hd, tcpname, AVIO_FLAG_READ_WRITE,
&h->interrupt_callback, NULL,
- h->protocol_whitelist, h->protocol_blacklist);
+ h->protocol_whitelist, h->protocol_blacklist, h);
if (err)
goto fail;
diff --git a/libavformat/rtmpcrypt.c b/libavformat/rtmpcrypt.c
index 3d5eb22..c41ae43 100644
--- a/libavformat/rtmpcrypt.c
+++ b/libavformat/rtmpcrypt.c
@@ -266,7 +266,7 @@ static int rtmpe_open(URLContext *h, const char *uri, int flags)
/* open the tcp or ffrtmphttp connection */
if ((ret = ffurl_open_whitelist(&rt->stream, url, AVIO_FLAG_READ_WRITE,
&h->interrupt_callback, NULL,
- h->protocol_whitelist, h->protocol_blacklist)) < 0) {
+ h->protocol_whitelist, h->protocol_blacklist, h)) < 0) {
rtmpe_close(h);
return ret;
}
diff --git a/libavformat/rtmpproto.c b/libavformat/rtmpproto.c
index c01bc40..18d915a 100644
--- a/libavformat/rtmpproto.c
+++ b/libavformat/rtmpproto.c
@@ -1120,7 +1120,7 @@ static int rtmp_calc_swfhash(URLContext *s)
/* Get the SWF player file. */
if ((ret = ffurl_open_whitelist(&stream, rt->swfverify, AVIO_FLAG_READ,
&s->interrupt_callback, NULL,
- s->protocol_whitelist, s->protocol_blacklist)) < 0) {
+ s->protocol_whitelist, s->protocol_blacklist, s)) < 0) {
av_log(s, AV_LOG_ERROR, "Cannot open connection %s.\n", rt->swfverify);
goto fail;
}
@@ -2650,7 +2650,7 @@ static int rtmp_open(URLContext *s, const char *uri, int flags)
reconnect:
if ((ret = ffurl_open_whitelist(&rt->stream, buf, AVIO_FLAG_READ_WRITE,
&s->interrupt_callback, &opts,
- s->protocol_whitelist, s->protocol_blacklist)) < 0) {
+ s->protocol_whitelist, s->protocol_blacklist, s)) < 0) {
av_log(s , AV_LOG_ERROR, "Cannot open connection %s\n", buf);
goto fail;
}
diff --git a/libavformat/rtpproto.c b/libavformat/rtpproto.c
index 2062083..fa1dcb5 100644
--- a/libavformat/rtpproto.c
+++ b/libavformat/rtpproto.c
@@ -382,7 +382,7 @@ static int rtp_open(URLContext *h, const char *uri, int flags)
hostname, rtp_port, s->local_rtpport,
sources, block);
if (ffurl_open_whitelist(&s->rtp_hd, buf, flags, &h->interrupt_callback,
- NULL, h->protocol_whitelist, h->protocol_blacklist) < 0)
+ NULL, h->protocol_whitelist, h->protocol_blacklist, h) < 0)
goto fail;
s->local_rtpport = ff_udp_get_local_port(s->rtp_hd);
if(s->local_rtpport == 65535) {
@@ -397,7 +397,7 @@ static int rtp_open(URLContext *h, const char *uri, int flags)
sources, block);
if (ffurl_open_whitelist(&s->rtcp_hd, buf, rtcpflags,
&h->interrupt_callback, NULL,
- h->protocol_whitelist, h->protocol_blacklist) < 0) {
+ h->protocol_whitelist, h->protocol_blacklist, h) < 0) {
s->local_rtpport = s->local_rtcpport = -1;
continue;
}
@@ -407,7 +407,7 @@ static int rtp_open(URLContext *h, const char *uri, int flags)
hostname, s->rtcp_port, s->local_rtcpport,
sources, block);
if (ffurl_open_whitelist(&s->rtcp_hd, buf, rtcpflags, &h->interrupt_callback,
- NULL, h->protocol_whitelist, h->protocol_blacklist) < 0)
+ NULL, h->protocol_whitelist, h->protocol_blacklist, h) < 0)
goto fail;
break;
}
diff --git a/libavformat/rtsp.c b/libavformat/rtsp.c
index bbff70b..6888a2b 100644
--- a/libavformat/rtsp.c
+++ b/libavformat/rtsp.c
@@ -1469,7 +1469,7 @@ int ff_rtsp_make_setup_request(AVFormatContext *s, const char *host, int port,
/* we will use two ports per rtp stream (rtp and rtcp) */
j += 2;
err = ffurl_open_whitelist(&rtsp_st->rtp_handle, buf, AVIO_FLAG_READ_WRITE,
- &s->interrupt_callback, &opts, s->protocol_whitelist, s->protocol_blacklist);
+ &s->interrupt_callback, &opts, s->protocol_whitelist, s->protocol_blacklist, NULL);
av_dict_free(&opts);
@@ -1612,7 +1612,7 @@ int ff_rtsp_make_setup_request(AVFormatContext *s, const char *host, int port,
ff_url_join(url, sizeof(url), "rtp", NULL, namebuf,
port, "%s", optbuf);
if (ffurl_open_whitelist(&rtsp_st->rtp_handle, url, AVIO_FLAG_READ_WRITE,
- &s->interrupt_callback, NULL, s->protocol_whitelist, s->protocol_blacklist) < 0) {
+ &s->interrupt_callback, NULL, s->protocol_whitelist, s->protocol_blacklist, NULL) < 0) {
err = AVERROR_INVALIDDATA;
goto fail;
}
@@ -1801,7 +1801,7 @@ redirect:
host, port,
"?timeout=%d", rt->stimeout);
if ((ret = ffurl_open_whitelist(&rt->rtsp_hd, tcpname, AVIO_FLAG_READ_WRITE,
- &s->interrupt_callback, NULL, s->protocol_whitelist, s->protocol_blacklist)) < 0) {
+ &s->interrupt_callback, NULL, s->protocol_whitelist, s->protocol_blacklist, NULL)) < 0) {
err = ret;
goto fail;
}
@@ -2317,7 +2317,7 @@ static int sdp_read_header(AVFormatContext *s)
rtsp_st->nb_exclude_source_addrs,
rtsp_st->exclude_source_addrs);
err = ffurl_open_whitelist(&rtsp_st->rtp_handle, url, AVIO_FLAG_READ,
- &s->interrupt_callback, &opts, s->protocol_whitelist, s->protocol_blacklist);
+ &s->interrupt_callback, &opts, s->protocol_whitelist, s->protocol_blacklist, NULL);
av_dict_free(&opts);
@@ -2387,7 +2387,7 @@ static int rtp_read_header(AVFormatContext *s)
return AVERROR(EIO);
ret = ffurl_open_whitelist(&in, s->filename, AVIO_FLAG_READ,
- &s->interrupt_callback, NULL, s->protocol_whitelist, s->protocol_blacklist);
+ &s->interrupt_callback, NULL, s->protocol_whitelist, s->protocol_blacklist, NULL);
if (ret)
goto fail;
diff --git a/libavformat/rtspdec.c b/libavformat/rtspdec.c
index ca7acc8..a722b98 100644
--- a/libavformat/rtspdec.c
+++ b/libavformat/rtspdec.c
@@ -296,7 +296,7 @@ static int rtsp_read_setup(AVFormatContext *s, char* host, char *controlurl)
av_log(s, AV_LOG_TRACE, "Opening: %s", url);
ret = ffurl_open_whitelist(&rtsp_st->rtp_handle, url, AVIO_FLAG_READ_WRITE,
&s->interrupt_callback, &opts,
- s->protocol_whitelist, s->protocol_blacklist);
+ s->protocol_whitelist, s->protocol_blacklist, NULL);
av_dict_free(&opts);
if (ret)
localport += 2;
@@ -665,7 +665,7 @@ static int rtsp_listen(AVFormatContext *s)
if (ret = ffurl_open_whitelist(&rt->rtsp_hd, tcpname, AVIO_FLAG_READ_WRITE,
&s->interrupt_callback, NULL,
- s->protocol_whitelist, s->protocol_blacklist)) {
+ s->protocol_whitelist, s->protocol_blacklist, NULL)) {
av_log(s, AV_LOG_ERROR, "Unable to open RTSP for listening\n");
return ret;
}
diff --git a/libavformat/sapdec.c b/libavformat/sapdec.c
index 218c32a..522b38d 100644
--- a/libavformat/sapdec.c
+++ b/libavformat/sapdec.c
@@ -87,7 +87,7 @@ static int sap_read_header(AVFormatContext *s)
port);
ret = ffurl_open_whitelist(&sap->ann_fd, url, AVIO_FLAG_READ,
&s->interrupt_callback, NULL,
- s->protocol_whitelist, s->protocol_blacklist);
+ s->protocol_whitelist, s->protocol_blacklist, NULL);
if (ret)
goto fail;
diff --git a/libavformat/sapenc.c b/libavformat/sapenc.c
index 0699e3e..3098e34 100644
--- a/libavformat/sapenc.c
+++ b/libavformat/sapenc.c
@@ -151,7 +151,7 @@ static int sap_write_header(AVFormatContext *s)
base_port += 2;
ret = ffurl_open_whitelist(&fd, url, AVIO_FLAG_WRITE,
&s->interrupt_callback, NULL,
- s->protocol_whitelist, s->protocol_blacklist);
+ s->protocol_whitelist, s->protocol_blacklist, NULL);
if (ret) {
ret = AVERROR(EIO);
goto fail;
@@ -171,7 +171,7 @@ static int sap_write_header(AVFormatContext *s)
"?ttl=%d&connect=1", ttl);
ret = ffurl_open_whitelist(&sap->ann_fd, url, AVIO_FLAG_WRITE,
&s->interrupt_callback, NULL,
- s->protocol_whitelist, s->protocol_blacklist);
+ s->protocol_whitelist, s->protocol_blacklist, NULL);
if (ret) {
ret = AVERROR(EIO);
goto fail;
diff --git a/libavformat/smoothstreamingenc.c b/libavformat/smoothstreamingenc.c
index 193c360..dabd1ea 100644
--- a/libavformat/smoothstreamingenc.c
+++ b/libavformat/smoothstreamingenc.c
@@ -123,7 +123,7 @@ static int64_t ism_seek(void *opaque, int64_t offset, int whence)
os->tail_out = os->out;
av_dict_set(&opts, "truncate", "0", 0);
ret = ffurl_open_whitelist(&os->out, frag->file, AVIO_FLAG_WRITE,
- &os->ctx->interrupt_callback, &opts, os->ctx->protocol_whitelist, os->ctx->protocol_blacklist);
+ &os->ctx->interrupt_callback, &opts, os->ctx->protocol_whitelist, os->ctx->protocol_blacklist, NULL);
av_dict_free(&opts);
if (ret < 0) {
os->out = os->tail_out;
@@ -132,7 +132,7 @@ static int64_t ism_seek(void *opaque, int64_t offset, int whence)
}
av_dict_set(&opts, "truncate", "0", 0);
ffurl_open_whitelist(&os->out2, frag->infofile, AVIO_FLAG_WRITE,
- &os->ctx->interrupt_callback, &opts, os->ctx->protocol_whitelist, os->ctx->protocol_blacklist);
+ &os->ctx->interrupt_callback, &opts, os->ctx->protocol_whitelist, os->ctx->protocol_blacklist, NULL);
av_dict_free(&opts);
ffurl_seek(os->out, offset - frag->start_pos, SEEK_SET);
if (os->out2)
@@ -526,7 +526,7 @@ static int ism_flush(AVFormatContext *s, int final)
continue;
snprintf(filename, sizeof(filename), "%s/temp", os->dirname);
- ret = ffurl_open_whitelist(&os->out, filename, AVIO_FLAG_WRITE, &s->interrupt_callback, NULL, s->protocol_whitelist, s->protocol_blacklist);
+ ret = ffurl_open_whitelist(&os->out, filename, AVIO_FLAG_WRITE, &s->interrupt_callback, NULL, s->protocol_whitelist, s->protocol_blacklist, NULL);
if (ret < 0)
break;
os->cur_start_pos = os->tail_pos;
diff --git a/libavformat/srtpproto.c b/libavformat/srtpproto.c
index ef87c08..5e6e516 100644
--- a/libavformat/srtpproto.c
+++ b/libavformat/srtpproto.c
@@ -81,7 +81,7 @@ static int srtp_open(URLContext *h, const char *uri, int flags)
path, sizeof(path), uri);
ff_url_join(buf, sizeof(buf), "rtp", NULL, hostname, rtp_port, "%s", path);
if ((ret = ffurl_open_whitelist(&s->rtp_hd, buf, flags, &h->interrupt_callback,
- NULL, h->protocol_whitelist, h->protocol_blacklist)) < 0)
+ NULL, h->protocol_whitelist, h->protocol_blacklist, h)) < 0)
goto fail;
h->max_packet_size = FFMIN(s->rtp_hd->max_packet_size,
diff --git a/libavformat/subfile.c b/libavformat/subfile.c
index fdd328a..fa971e1 100644
--- a/libavformat/subfile.c
+++ b/libavformat/subfile.c
@@ -78,7 +78,7 @@ static int subfile_open(URLContext *h, const char *filename, int flags,
}
av_strstart(filename, "subfile:", &filename);
ret = ffurl_open_whitelist(&c->h, filename, flags, &h->interrupt_callback,
- options, h->protocol_whitelist, h->protocol_blacklist);
+ options, h->protocol_whitelist, h->protocol_blacklist, h);
if (ret < 0)
return ret;
c->pos = c->start;
diff --git a/libavformat/tls.c b/libavformat/tls.c
index 2a59aa7..10e0792 100644
--- a/libavformat/tls.c
+++ b/libavformat/tls.c
@@ -106,5 +106,5 @@ int ff_tls_open_underlying(TLSShared *c, URLContext *parent, const char *uri, AV
return ffurl_open_whitelist(&c->tcp, buf, AVIO_FLAG_READ_WRITE,
&parent->interrupt_callback, options,
- parent->protocol_whitelist, parent->protocol_blacklist);
+ parent->protocol_whitelist, parent->protocol_blacklist, parent);
}
diff --git a/libavformat/url.h b/libavformat/url.h
index 4ce60cc..f19b91c 100644
--- a/libavformat/url.h
+++ b/libavformat/url.h
@@ -136,12 +136,15 @@ int ffurl_connect(URLContext *uc, AVDictionary **options);
* @param options A dictionary filled with protocol-private options. On return
* this parameter will be destroyed and replaced with a dict containing options
* that were not found. May be NULL.
+ * @param parent An enclosing URLContext, whose generic options should
+ * be applied to this URLContext as well.
* @return >= 0 in case of success, a negative value corresponding to an
* AVERROR code in case of failure
*/
int ffurl_open_whitelist(URLContext **puc, const char *filename, int flags,
const AVIOInterruptCB *int_cb, AVDictionary **options,
- const char *whitelist, const char* blacklist);
+ const char *whitelist, const char* blacklist,
+ URLContext *parent);
int ffurl_open(URLContext **puc, const char *filename, int flags,
const AVIOInterruptCB *int_cb, AVDictionary **options);
======================================================================
diff --cc libavformat/async.c
index 0cc6fb0,0000000..54dbd23
mode 100644,000000..100644
--- a/libavformat/async.c
+++ b/libavformat/async.c
@@@ -1,699 -1,0 +1,699 @@@
+/*
+ * Input async protocol.
+ * Copyright (c) 2015 Zhang Rui <bbcallen at gmail.com>
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Based on libavformat/cache.c by Michael Niedermayer
+ */
+
+ /**
+ * @TODO
+ * support timeout
+ * support work with concatdec, hls
+ */
+
+#include "libavutil/avassert.h"
+#include "libavutil/avstring.h"
+#include "libavutil/error.h"
+#include "libavutil/fifo.h"
+#include "libavutil/log.h"
+#include "libavutil/opt.h"
+#include "libavutil/thread.h"
+#include "url.h"
+#include <stdint.h>
+
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#define BUFFER_CAPACITY (4 * 1024 * 1024)
+#define READ_BACK_CAPACITY (4 * 1024 * 1024)
+#define SHORT_SEEK_THRESHOLD (256 * 1024)
+
+typedef struct RingBuffer
+{
+ AVFifoBuffer *fifo;
+ int read_back_capacity;
+
+ int read_pos;
+} RingBuffer;
+
+typedef struct Context {
+ AVClass *class;
+ URLContext *inner;
+
+ int seek_request;
+ int64_t seek_pos;
+ int seek_whence;
+ int seek_completed;
+ int64_t seek_ret;
+
+ int inner_io_error;
+ int io_error;
+ int io_eof_reached;
+
+ int64_t logical_pos;
+ int64_t logical_size;
+ RingBuffer ring;
+
+ pthread_cond_t cond_wakeup_main;
+ pthread_cond_t cond_wakeup_background;
+ pthread_mutex_t mutex;
+ pthread_t async_buffer_thread;
+
+ int abort_request;
+ AVIOInterruptCB interrupt_callback;
+} Context;
+
+static int ring_init(RingBuffer *ring, unsigned int capacity, int read_back_capacity)
+{
+ memset(ring, 0, sizeof(RingBuffer));
+ ring->fifo = av_fifo_alloc(capacity + read_back_capacity);
+ if (!ring->fifo)
+ return AVERROR(ENOMEM);
+
+ ring->read_back_capacity = read_back_capacity;
+ return 0;
+}
+
+static void ring_destroy(RingBuffer *ring)
+{
+ av_fifo_freep(&ring->fifo);
+}
+
+static void ring_reset(RingBuffer *ring)
+{
+ av_fifo_reset(ring->fifo);
+ ring->read_pos = 0;
+}
+
+static int ring_size(RingBuffer *ring)
+{
+ return av_fifo_size(ring->fifo) - ring->read_pos;
+}
+
+static int ring_space(RingBuffer *ring)
+{
+ return av_fifo_space(ring->fifo);
+}
+
+static int ring_generic_read(RingBuffer *ring, void *dest, int buf_size, void (*func)(void*, void*, int))
+{
+ int ret;
+
+ av_assert2(buf_size <= ring_size(ring));
+ ret = av_fifo_generic_peek_at(ring->fifo, dest, ring->read_pos, buf_size, func);
+ ring->read_pos += buf_size;
+
+ if (ring->read_pos > ring->read_back_capacity) {
+ av_fifo_drain(ring->fifo, ring->read_pos - ring->read_back_capacity);
+ ring->read_pos = ring->read_back_capacity;
+ }
+
+ return ret;
+}
+
+static int ring_generic_write(RingBuffer *ring, void *src, int size, int (*func)(void*, void*, int))
+{
+ av_assert2(size <= ring_space(ring));
+ return av_fifo_generic_write(ring->fifo, src, size, func);
+}
+
+static int ring_size_of_read_back(RingBuffer *ring)
+{
+ return ring->read_pos;
+}
+
+static int ring_drain(RingBuffer *ring, int offset)
+{
+ av_assert2(offset >= -ring_size_of_read_back(ring));
+ av_assert2(offset <= -ring_size(ring));
+ ring->read_pos += offset;
+ return 0;
+}
+
+static int async_check_interrupt(void *arg)
+{
+ URLContext *h = arg;
+ Context *c = h->priv_data;
+
+ if (c->abort_request)
+ return 1;
+
+ if (ff_check_interrupt(&c->interrupt_callback))
+ c->abort_request = 1;
+
+ return c->abort_request;
+}
+
+static int wrapped_url_read(void *src, void *dst, int size)
+{
+ URLContext *h = src;
+ Context *c = h->priv_data;
+ int ret;
+
+ ret = ffurl_read(c->inner, dst, size);
+ c->inner_io_error = ret < 0 ? ret : 0;
+
+ return ret;
+}
+
+static void *async_buffer_task(void *arg)
+{
+ URLContext *h = arg;
+ Context *c = h->priv_data;
+ RingBuffer *ring = &c->ring;
+ int ret = 0;
+ int64_t seek_ret;
+
+ while (1) {
+ int fifo_space, to_copy;
+
+ pthread_mutex_lock(&c->mutex);
+ if (async_check_interrupt(h)) {
+ c->io_eof_reached = 1;
+ c->io_error = AVERROR_EXIT;
+ pthread_cond_signal(&c->cond_wakeup_main);
+ pthread_mutex_unlock(&c->mutex);
+ break;
+ }
+
+ if (c->seek_request) {
+ seek_ret = ffurl_seek(c->inner, c->seek_pos, c->seek_whence);
+ if (seek_ret >= 0) {
+ c->io_eof_reached = 0;
+ c->io_error = 0;
+ ring_reset(ring);
+ }
+
+ c->seek_completed = 1;
+ c->seek_ret = seek_ret;
+ c->seek_request = 0;
+
+
+ pthread_cond_signal(&c->cond_wakeup_main);
+ pthread_mutex_unlock(&c->mutex);
+ continue;
+ }
+
+ fifo_space = ring_space(ring);
+ if (c->io_eof_reached || fifo_space <= 0) {
+ pthread_cond_signal(&c->cond_wakeup_main);
+ pthread_cond_wait(&c->cond_wakeup_background, &c->mutex);
+ pthread_mutex_unlock(&c->mutex);
+ continue;
+ }
+ pthread_mutex_unlock(&c->mutex);
+
+ to_copy = FFMIN(4096, fifo_space);
+ ret = ring_generic_write(ring, (void *)h, to_copy, wrapped_url_read);
+
+ pthread_mutex_lock(&c->mutex);
+ if (ret <= 0) {
+ c->io_eof_reached = 1;
+ if (c->inner_io_error < 0)
+ c->io_error = c->inner_io_error;
+ }
+
+ pthread_cond_signal(&c->cond_wakeup_main);
+ pthread_mutex_unlock(&c->mutex);
+ }
+
+ return NULL;
+}
+
+static int async_open(URLContext *h, const char *arg, int flags, AVDictionary **options)
+{
+ Context *c = h->priv_data;
+ int ret;
+ AVIOInterruptCB interrupt_callback = {.callback = async_check_interrupt, .opaque = h};
+
+ av_strstart(arg, "async:", &arg);
+
+ ret = ring_init(&c->ring, BUFFER_CAPACITY, READ_BACK_CAPACITY);
+ if (ret < 0)
+ goto fifo_fail;
+
+ /* wrap interrupt callback */
+ c->interrupt_callback = h->interrupt_callback;
- ret = ffurl_open_whitelist(&c->inner, arg, flags, &interrupt_callback, options, h->protocol_whitelist, h->protocol_blacklist);
++ ret = ffurl_open_whitelist(&c->inner, arg, flags, &interrupt_callback, options, h->protocol_whitelist, h->protocol_blacklist, h);
+ if (ret != 0) {
+ av_log(h, AV_LOG_ERROR, "ffurl_open failed : %s, %s\n", av_err2str(ret), arg);
+ goto url_fail;
+ }
+
+ c->logical_size = ffurl_size(c->inner);
+ h->is_streamed = c->inner->is_streamed;
+
+ ret = pthread_mutex_init(&c->mutex, NULL);
+ if (ret != 0) {
+ av_log(h, AV_LOG_ERROR, "pthread_mutex_init failed : %s\n", av_err2str(ret));
+ goto mutex_fail;
+ }
+
+ ret = pthread_cond_init(&c->cond_wakeup_main, NULL);
+ if (ret != 0) {
+ av_log(h, AV_LOG_ERROR, "pthread_cond_init failed : %s\n", av_err2str(ret));
+ goto cond_wakeup_main_fail;
+ }
+
+ ret = pthread_cond_init(&c->cond_wakeup_background, NULL);
+ if (ret != 0) {
+ av_log(h, AV_LOG_ERROR, "pthread_cond_init failed : %s\n", av_err2str(ret));
+ goto cond_wakeup_background_fail;
+ }
+
+ ret = pthread_create(&c->async_buffer_thread, NULL, async_buffer_task, h);
+ if (ret) {
+ av_log(h, AV_LOG_ERROR, "pthread_create failed : %s\n", av_err2str(ret));
+ goto thread_fail;
+ }
+
+ return 0;
+
+thread_fail:
+ pthread_cond_destroy(&c->cond_wakeup_background);
+cond_wakeup_background_fail:
+ pthread_cond_destroy(&c->cond_wakeup_main);
+cond_wakeup_main_fail:
+ pthread_mutex_destroy(&c->mutex);
+mutex_fail:
+ ffurl_close(c->inner);
+url_fail:
+ ring_destroy(&c->ring);
+fifo_fail:
+ return ret;
+}
+
+static int async_close(URLContext *h)
+{
+ Context *c = h->priv_data;
+ int ret;
+
+ pthread_mutex_lock(&c->mutex);
+ c->abort_request = 1;
+ pthread_cond_signal(&c->cond_wakeup_background);
+ pthread_mutex_unlock(&c->mutex);
+
+ ret = pthread_join(c->async_buffer_thread, NULL);
+ if (ret != 0)
+ av_log(h, AV_LOG_ERROR, "pthread_join(): %s\n", av_err2str(ret));
+
+ pthread_cond_destroy(&c->cond_wakeup_background);
+ pthread_cond_destroy(&c->cond_wakeup_main);
+ pthread_mutex_destroy(&c->mutex);
+ ffurl_close(c->inner);
+ ring_destroy(&c->ring);
+
+ return 0;
+}
+
+static int async_read_internal(URLContext *h, void *dest, int size, int read_complete,
+ void (*func)(void*, void*, int))
+{
+ Context *c = h->priv_data;
+ RingBuffer *ring = &c->ring;
+ int to_read = size;
+ int ret = 0;
+
+ pthread_mutex_lock(&c->mutex);
+
+ while (to_read > 0) {
+ int fifo_size, to_copy;
+ if (async_check_interrupt(h)) {
+ ret = AVERROR_EXIT;
+ break;
+ }
+ fifo_size = ring_size(ring);
+ to_copy = FFMIN(to_read, fifo_size);
+ if (to_copy > 0) {
+ ring_generic_read(ring, dest, to_copy, func);
+ if (!func)
+ dest = (uint8_t *)dest + to_copy;
+ c->logical_pos += to_copy;
+ to_read -= to_copy;
+ ret = size - to_read;
+
+ if (to_read <= 0 || !read_complete)
+ break;
+ } else if (c->io_eof_reached) {
+ if (ret <= 0) {
+ if (c->io_error)
+ ret = c->io_error;
+ else
+ ret = AVERROR_EOF;
+ }
+ break;
+ }
+ pthread_cond_signal(&c->cond_wakeup_background);
+ pthread_cond_wait(&c->cond_wakeup_main, &c->mutex);
+ }
+
+ pthread_cond_signal(&c->cond_wakeup_background);
+ pthread_mutex_unlock(&c->mutex);
+
+ return ret;
+}
+
+static int async_read(URLContext *h, unsigned char *buf, int size)
+{
+ return async_read_internal(h, buf, size, 0, NULL);
+}
+
+static void fifo_do_not_copy_func(void* dest, void* src, int size) {
+ // do not copy
+}
+
+static int64_t async_seek(URLContext *h, int64_t pos, int whence)
+{
+ Context *c = h->priv_data;
+ RingBuffer *ring = &c->ring;
+ int64_t ret;
+ int64_t new_logical_pos;
+ int fifo_size;
+ int fifo_size_of_read_back;
+
+ if (whence == AVSEEK_SIZE) {
+ av_log(h, AV_LOG_TRACE, "async_seek: AVSEEK_SIZE: %"PRId64"\n", (int64_t)c->logical_size);
+ return c->logical_size;
+ } else if (whence == SEEK_CUR) {
+ av_log(h, AV_LOG_TRACE, "async_seek: %"PRId64"\n", pos);
+ new_logical_pos = pos + c->logical_pos;
+ } else if (whence == SEEK_SET){
+ av_log(h, AV_LOG_TRACE, "async_seek: %"PRId64"\n", pos);
+ new_logical_pos = pos;
+ } else {
+ return AVERROR(EINVAL);
+ }
+ if (new_logical_pos < 0)
+ return AVERROR(EINVAL);
+
+ fifo_size = ring_size(ring);
+ fifo_size_of_read_back = ring_size_of_read_back(ring);
+ if (new_logical_pos == c->logical_pos) {
+ /* current position */
+ return c->logical_pos;
+ } else if ((new_logical_pos >= (c->logical_pos - fifo_size_of_read_back)) &&
+ (new_logical_pos < (c->logical_pos + fifo_size + SHORT_SEEK_THRESHOLD))) {
+ int pos_delta = (int)(new_logical_pos - c->logical_pos);
+ /* fast seek */
+ av_log(h, AV_LOG_TRACE, "async_seek: fask_seek %"PRId64" from %d dist:%d/%d\n",
+ new_logical_pos, (int)c->logical_pos,
+ (int)(new_logical_pos - c->logical_pos), fifo_size);
+
+ if (pos_delta > 0) {
+ // fast seek forwards
+ async_read_internal(h, NULL, pos_delta, 1, fifo_do_not_copy_func);
+ } else {
+ // fast seek backwards
+ ring_drain(ring, pos_delta);
+ c->logical_pos = new_logical_pos;
+ }
+
+ return c->logical_pos;
+ } else if (c->logical_size <= 0) {
+ /* can not seek */
+ return AVERROR(EINVAL);
+ } else if (new_logical_pos > c->logical_size) {
+ /* beyond end */
+ return AVERROR(EINVAL);
+ }
+
+ pthread_mutex_lock(&c->mutex);
+
+ c->seek_request = 1;
+ c->seek_pos = new_logical_pos;
+ c->seek_whence = SEEK_SET;
+ c->seek_completed = 0;
+ c->seek_ret = 0;
+
+ while (1) {
+ if (async_check_interrupt(h)) {
+ ret = AVERROR_EXIT;
+ break;
+ }
+ if (c->seek_completed) {
+ if (c->seek_ret >= 0)
+ c->logical_pos = c->seek_ret;
+ ret = c->seek_ret;
+ break;
+ }
+ pthread_cond_signal(&c->cond_wakeup_background);
+ pthread_cond_wait(&c->cond_wakeup_main, &c->mutex);
+ }
+
+ pthread_mutex_unlock(&c->mutex);
+
+ return ret;
+}
+
+#define OFFSET(x) offsetof(Context, x)
+#define D AV_OPT_FLAG_DECODING_PARAM
+
+static const AVOption options[] = {
+ {NULL},
+};
+
+#undef D
+#undef OFFSET
+
+static const AVClass async_context_class = {
+ .class_name = "Async",
+ .item_name = av_default_item_name,
+ .option = options,
+ .version = LIBAVUTIL_VERSION_INT,
+};
+
+const URLProtocol ff_async_protocol = {
+ .name = "async",
+ .url_open2 = async_open,
+ .url_read = async_read,
+ .url_seek = async_seek,
+ .url_close = async_close,
+ .priv_data_size = sizeof(Context),
+ .priv_data_class = &async_context_class,
+};
+
+#if 0
+
+#define TEST_SEEK_POS (1536)
+#define TEST_STREAM_SIZE (2048)
+
+typedef struct TestContext {
+ AVClass *class;
+ int64_t logical_pos;
+ int64_t logical_size;
+
+ /* options */
+ int opt_read_error;
+} TestContext;
+
+static int async_test_open(URLContext *h, const char *arg, int flags, AVDictionary **options)
+{
+ TestContext *c = h->priv_data;
+ c->logical_pos = 0;
+ c->logical_size = TEST_STREAM_SIZE;
+ return 0;
+}
+
+static int async_test_close(URLContext *h)
+{
+ return 0;
+}
+
+static int async_test_read(URLContext *h, unsigned char *buf, int size)
+{
+ TestContext *c = h->priv_data;
+ int i;
+ int read_len = 0;
+
+ if (c->opt_read_error)
+ return c->opt_read_error;
+
+ if (c->logical_pos >= c->logical_size)
+ return AVERROR_EOF;
+
+ for (i = 0; i < size; ++i) {
+ buf[i] = c->logical_pos & 0xFF;
+
+ c->logical_pos++;
+ read_len++;
+
+ if (c->logical_pos >= c->logical_size)
+ break;
+ }
+
+ return read_len;
+}
+
+static int64_t async_test_seek(URLContext *h, int64_t pos, int whence)
+{
+ TestContext *c = h->priv_data;
+ int64_t new_logical_pos;
+
+ if (whence == AVSEEK_SIZE) {
+ return c->logical_size;
+ } else if (whence == SEEK_CUR) {
+ new_logical_pos = pos + c->logical_pos;
+ } else if (whence == SEEK_SET){
+ new_logical_pos = pos;
+ } else {
+ return AVERROR(EINVAL);
+ }
+ if (new_logical_pos < 0)
+ return AVERROR(EINVAL);
+
+ c->logical_pos = new_logical_pos;
+ return new_logical_pos;
+}
+
+#define OFFSET(x) offsetof(TestContext, x)
+#define D AV_OPT_FLAG_DECODING_PARAM
+
+static const AVOption async_test_options[] = {
+ { "async-test-read-error", "cause read fail",
+ OFFSET(opt_read_error), AV_OPT_TYPE_INT, { .i64 = 0 }, INT_MIN, INT_MAX, .flags = D },
+ {NULL},
+};
+
+#undef D
+#undef OFFSET
+
+static const AVClass async_test_context_class = {
+ .class_name = "Async-Test",
+ .item_name = av_default_item_name,
+ .option = async_test_options,
+ .version = LIBAVUTIL_VERSION_INT,
+};
+
+const URLProtocol ff_async_test_protocol = {
+ .name = "async-test",
+ .url_open2 = async_test_open,
+ .url_read = async_test_read,
+ .url_seek = async_test_seek,
+ .url_close = async_test_close,
+ .priv_data_size = sizeof(TestContext),
+ .priv_data_class = &async_test_context_class,
+};
+
+int main(void)
+{
+ URLContext *h = NULL;
+ int i;
+ int ret;
+ int64_t size;
+ int64_t pos;
+ int64_t read_len;
+ unsigned char buf[4096];
+ AVDictionary *opts = NULL;
+
+ ffurl_register_protocol(&ff_async_protocol);
+ ffurl_register_protocol(&ff_async_test_protocol);
+
+ /*
+ * test normal read
+ */
+ ret = ffurl_open(&h, "async:async-test:", AVIO_FLAG_READ, NULL, NULL);
+ printf("open: %d\n", ret);
+
+ size = ffurl_size(h);
+ printf("size: %"PRId64"\n", size);
+
+ pos = ffurl_seek(h, 0, SEEK_CUR);
+ read_len = 0;
+ while (1) {
+ ret = ffurl_read(h, buf, sizeof(buf));
+ if (ret == AVERROR_EOF) {
+ printf("read-error: AVERROR_EOF at %"PRId64"\n", ffurl_seek(h, 0, SEEK_CUR));
+ break;
+ }
+ else if (ret == 0)
+ break;
+ else if (ret < 0) {
+ printf("read-error: %d at %"PRId64"\n", ret, ffurl_seek(h, 0, SEEK_CUR));
+ goto fail;
+ } else {
+ for (i = 0; i < ret; ++i) {
+ if (buf[i] != (pos & 0xFF)) {
+ printf("read-mismatch: actual %d, expecting %d, at %"PRId64"\n",
+ (int)buf[i], (int)(pos & 0xFF), pos);
+ break;
+ }
+ pos++;
+ }
+ }
+
+ read_len += ret;
+ }
+ printf("read: %"PRId64"\n", read_len);
+
+ /*
+ * test normal seek
+ */
+ ret = ffurl_read(h, buf, 1);
+ printf("read: %d\n", ret);
+
+ pos = ffurl_seek(h, TEST_SEEK_POS, SEEK_SET);
+ printf("seek: %"PRId64"\n", pos);
+
+ read_len = 0;
+ while (1) {
+ ret = ffurl_read(h, buf, sizeof(buf));
+ if (ret == AVERROR_EOF)
+ break;
+ else if (ret == 0)
+ break;
+ else if (ret < 0) {
+ printf("read-error: %d at %"PRId64"\n", ret, ffurl_seek(h, 0, SEEK_CUR));
+ goto fail;
+ } else {
+ for (i = 0; i < ret; ++i) {
+ if (buf[i] != (pos & 0xFF)) {
+ printf("read-mismatch: actual %d, expecting %d, at %"PRId64"\n",
+ (int)buf[i], (int)(pos & 0xFF), pos);
+ break;
+ }
+ pos++;
+ }
+ }
+
+ read_len += ret;
+ }
+ printf("read: %"PRId64"\n", read_len);
+
+ ret = ffurl_read(h, buf, 1);
+ printf("read: %d\n", ret);
+
+ /*
+ * test read error
+ */
+ ffurl_close(h);
+ av_dict_set_int(&opts, "async-test-read-error", -10000, 0);
+ ret = ffurl_open(&h, "async:async-test:", AVIO_FLAG_READ, NULL, &opts);
+ printf("open: %d\n", ret);
+
+ ret = ffurl_read(h, buf, 1);
+ printf("read: %d\n", ret);
+
+fail:
+ av_dict_free(&opts);
+ ffurl_close(h);
+ return 0;
+}
+
+#endif
diff --cc libavformat/avio.c
index 7e68c9a,1692f1b..6507593
--- a/libavformat/avio.c
+++ b/libavformat/avio.c
@@@ -303,15 -172,16 +303,18 @@@ int ffurl_alloc(URLContext **puc, cons
return AVERROR_PROTOCOL_NOT_FOUND;
}
-int ffurl_open(URLContext **puc, const char *filename, int flags,
- const AVIOInterruptCB *int_cb, AVDictionary **options,
- const URLProtocol **protocols,
- URLContext *parent)
+int ffurl_open_whitelist(URLContext **puc, const char *filename, int flags,
+ const AVIOInterruptCB *int_cb, AVDictionary **options,
- const char *whitelist, const char* blacklist)
++ const char *whitelist, const char* blacklist,
++ URLContext *parent)
{
- int ret = ffurl_alloc(puc, filename, flags, int_cb, protocols);
- if (ret)
+ AVDictionary *tmp_opts = NULL;
+ AVDictionaryEntry *e;
+ int ret = ffurl_alloc(puc, filename, flags, int_cb);
+ if (ret < 0)
return ret;
+ if (parent)
+ av_opt_copy(*puc, parent);
if (options &&
(ret = av_opt_set_dict(*puc, options)) < 0)
goto fail;
@@@ -348,13 -197,6 +351,13 @@@ fail
return ret;
}
+int ffurl_open(URLContext **puc, const char *filename, int flags,
+ const AVIOInterruptCB *int_cb, AVDictionary **options)
+{
+ return ffurl_open_whitelist(puc, filename, flags,
- int_cb, options, NULL, NULL);
++ int_cb, options, NULL, NULL, NULL);
+}
+
static inline int retry_transfer_wrapper(URLContext *h, uint8_t *buf,
int size, int size_min,
int (*transfer_func)(URLContext *h,
diff --cc libavformat/aviobuf.c
index f2f03f6,29fccbe..f02ae21
--- a/libavformat/aviobuf.c
+++ b/libavformat/aviobuf.c
@@@ -989,9 -888,29 +989,9 @@@ int ffio_open_whitelist(AVIOContext **s
URLContext *h;
int err;
- err = ffurl_open_whitelist(&h, filename, flags, int_cb, options, whitelist, blacklist);
- if (options) {
- e = av_dict_get(*options, "protocol_whitelist", NULL, 0);
- if (e)
- proto_whitelist = e->value;
- e = av_dict_get(*options, "protocol_blacklist", NULL, 0);
- if (e)
- proto_blacklist = e->value;
- }
-
- protocols = ffurl_get_protocols(proto_whitelist, proto_blacklist);
- if (!protocols)
- return AVERROR(ENOMEM);
-
- err = ffurl_open(&h, filename, flags, int_cb, options, protocols, NULL);
- if (err < 0) {
- av_freep(&protocols);
++ err = ffurl_open_whitelist(&h, filename, flags, int_cb, options, whitelist, blacklist, NULL);
+ if (err < 0)
return err;
- }
-
err = ffio_fdopen(s, h);
if (err < 0) {
ffurl_close(h);
diff --cc libavformat/cache.c
index 25b28d5,0000000..6aabca2
mode 100644,000000..100644
--- a/libavformat/cache.c
+++ b/libavformat/cache.c
@@@ -1,330 -1,0 +1,330 @@@
+/*
+ * Input cache protocol.
+ * Copyright (c) 2011,2014 Michael Niedermayer
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Based on file.c by Fabrice Bellard
+ */
+
+/**
+ * @TODO
+ * support keeping files
+ * support filling with a background thread
+ */
+
+#include "libavutil/avassert.h"
+#include "libavutil/avstring.h"
+#include "libavutil/internal.h"
+#include "libavutil/opt.h"
+#include "libavutil/tree.h"
+#include "avformat.h"
+#include <fcntl.h>
+#if HAVE_IO_H
+#include <io.h>
+#endif
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <sys/stat.h>
+#include <stdlib.h>
+#include "os_support.h"
+#include "url.h"
+
+typedef struct CacheEntry {
+ int64_t logical_pos;
+ int64_t physical_pos;
+ int size;
+} CacheEntry;
+
+typedef struct Context {
+ AVClass *class;
+ int fd;
+ struct AVTreeNode *root;
+ int64_t logical_pos;
+ int64_t cache_pos;
+ int64_t inner_pos;
+ int64_t end;
+ int is_true_eof;
+ URLContext *inner;
+ int64_t cache_hit, cache_miss;
+ int read_ahead_limit;
+} Context;
+
+static int cmp(const void *key, const void *node)
+{
+ return FFDIFFSIGN(*(const int64_t *)key, ((const CacheEntry *) node)->logical_pos);
+}
+
+static int cache_open(URLContext *h, const char *arg, int flags, AVDictionary **options)
+{
+ char *buffername;
+ Context *c= h->priv_data;
+
+ av_strstart(arg, "cache:", &arg);
+
+ c->fd = avpriv_tempfile("ffcache", &buffername, 0, h);
+ if (c->fd < 0){
+ av_log(h, AV_LOG_ERROR, "Failed to create tempfile\n");
+ return c->fd;
+ }
+
+ unlink(buffername);
+ av_freep(&buffername);
+
+ return ffurl_open_whitelist(&c->inner, arg, flags, &h->interrupt_callback,
- options, h->protocol_whitelist, h->protocol_blacklist);
++ options, h->protocol_whitelist, h->protocol_blacklist, h);
+}
+
+static int add_entry(URLContext *h, const unsigned char *buf, int size)
+{
+ Context *c= h->priv_data;
+ int64_t pos = -1;
+ int ret;
+ CacheEntry *entry = NULL, *next[2] = {NULL, NULL};
+ CacheEntry *entry_ret;
+ struct AVTreeNode *node = NULL;
+
+ //FIXME avoid lseek
+ pos = lseek(c->fd, 0, SEEK_END);
+ if (pos < 0) {
+ ret = AVERROR(errno);
+ av_log(h, AV_LOG_ERROR, "seek in cache failed\n");
+ goto fail;
+ }
+ c->cache_pos = pos;
+
+ ret = write(c->fd, buf, size);
+ if (ret < 0) {
+ ret = AVERROR(errno);
+ av_log(h, AV_LOG_ERROR, "write in cache failed\n");
+ goto fail;
+ }
+ c->cache_pos += ret;
+
+ entry = av_tree_find(c->root, &c->logical_pos, cmp, (void**)next);
+
+ if (!entry)
+ entry = next[0];
+
+ if (!entry ||
+ entry->logical_pos + entry->size != c->logical_pos ||
+ entry->physical_pos + entry->size != pos
+ ) {
+ entry = av_malloc(sizeof(*entry));
+ node = av_tree_node_alloc();
+ if (!entry || !node) {
+ ret = AVERROR(ENOMEM);
+ goto fail;
+ }
+ entry->logical_pos = c->logical_pos;
+ entry->physical_pos = pos;
+ entry->size = ret;
+
+ entry_ret = av_tree_insert(&c->root, entry, cmp, &node);
+ if (entry_ret && entry_ret != entry) {
+ ret = -1;
+ av_log(h, AV_LOG_ERROR, "av_tree_insert failed\n");
+ goto fail;
+ }
+ } else
+ entry->size += ret;
+
+ return 0;
+fail:
+ //we could truncate the file to pos here if pos >=0 but ftruncate isn't available in VS so
+ //for simplicty we just leave the file a bit larger
+ av_free(entry);
+ av_free(node);
+ return ret;
+}
+
+static int cache_read(URLContext *h, unsigned char *buf, int size)
+{
+ Context *c= h->priv_data;
+ CacheEntry *entry, *next[2] = {NULL, NULL};
+ int64_t r;
+
+ entry = av_tree_find(c->root, &c->logical_pos, cmp, (void**)next);
+
+ if (!entry)
+ entry = next[0];
+
+ if (entry) {
+ int64_t in_block_pos = c->logical_pos - entry->logical_pos;
+ av_assert0(entry->logical_pos <= c->logical_pos);
+ if (in_block_pos < entry->size) {
+ int64_t physical_target = entry->physical_pos + in_block_pos;
+
+ if (c->cache_pos != physical_target) {
+ r = lseek(c->fd, physical_target, SEEK_SET);
+ } else
+ r = c->cache_pos;
+
+ if (r >= 0) {
+ c->cache_pos = r;
+ r = read(c->fd, buf, FFMIN(size, entry->size - in_block_pos));
+ }
+
+ if (r > 0) {
+ c->cache_pos += r;
+ c->logical_pos += r;
+ c->cache_hit ++;
+ return r;
+ }
+ }
+ }
+
+ // Cache miss or some kind of fault with the cache
+
+ if (c->logical_pos != c->inner_pos) {
+ r = ffurl_seek(c->inner, c->logical_pos, SEEK_SET);
+ if (r<0) {
+ av_log(h, AV_LOG_ERROR, "Failed to perform internal seek\n");
+ return r;
+ }
+ c->inner_pos = r;
+ }
+
+ r = ffurl_read(c->inner, buf, size);
+ if (r == 0 && size>0) {
+ c->is_true_eof = 1;
+ av_assert0(c->end >= c->logical_pos);
+ }
+ if (r<=0)
+ return r;
+ c->inner_pos += r;
+
+ c->cache_miss ++;
+
+ add_entry(h, buf, r);
+ c->logical_pos += r;
+ c->end = FFMAX(c->end, c->logical_pos);
+
+ return r;
+}
+
+static int64_t cache_seek(URLContext *h, int64_t pos, int whence)
+{
+ Context *c= h->priv_data;
+ int64_t ret;
+
+ if (whence == AVSEEK_SIZE) {
+ pos= ffurl_seek(c->inner, pos, whence);
+ if(pos <= 0){
+ pos= ffurl_seek(c->inner, -1, SEEK_END);
+ if (ffurl_seek(c->inner, c->inner_pos, SEEK_SET) < 0)
+ av_log(h, AV_LOG_ERROR, "Inner protocol failed to seekback end : %"PRId64"\n", pos);
+ }
+ if (pos > 0)
+ c->is_true_eof = 1;
+ c->end = FFMAX(c->end, pos);
+ return pos;
+ }
+
+ if (whence == SEEK_CUR) {
+ whence = SEEK_SET;
+ pos += c->logical_pos;
+ } else if (whence == SEEK_END && c->is_true_eof) {
+resolve_eof:
+ whence = SEEK_SET;
+ pos += c->end;
+ }
+
+ if (whence == SEEK_SET && pos >= 0 && pos < c->end) {
+ //Seems within filesize, assume it will not fail.
+ c->logical_pos = pos;
+ return pos;
+ }
+
+ //cache miss
+ ret= ffurl_seek(c->inner, pos, whence);
+ if ((whence == SEEK_SET && pos >= c->logical_pos ||
+ whence == SEEK_END && pos <= 0) && ret < 0) {
+ if ( (whence == SEEK_SET && c->read_ahead_limit >= pos - c->logical_pos)
+ || c->read_ahead_limit < 0) {
+ uint8_t tmp[32768];
+ while (c->logical_pos < pos || whence == SEEK_END) {
+ int size = sizeof(tmp);
+ if (whence == SEEK_SET)
+ size = FFMIN(sizeof(tmp), pos - c->logical_pos);
+ ret = cache_read(h, tmp, size);
+ if (ret == 0 && whence == SEEK_END) {
+ av_assert0(c->is_true_eof);
+ goto resolve_eof;
+ }
+ if (ret < 0) {
+ return ret;
+ }
+ }
+ return c->logical_pos;
+ }
+ }
+
+ if (ret >= 0) {
+ c->logical_pos = ret;
+ c->end = FFMAX(c->end, ret);
+ }
+
+ return ret;
+}
+
+static int enu_free(void *opaque, void *elem)
+{
+ av_free(elem);
+ return 0;
+}
+
+static int cache_close(URLContext *h)
+{
+ Context *c= h->priv_data;
+
+ av_log(h, AV_LOG_INFO, "Statistics, cache hits:%"PRId64" cache misses:%"PRId64"\n",
+ c->cache_hit, c->cache_miss);
+
+ close(c->fd);
+ ffurl_close(c->inner);
+ av_tree_enumerate(c->root, NULL, NULL, enu_free);
+ av_tree_destroy(c->root);
+
+ return 0;
+}
+
+#define OFFSET(x) offsetof(Context, x)
+#define D AV_OPT_FLAG_DECODING_PARAM
+
+static const AVOption options[] = {
+ { "read_ahead_limit", "Amount in bytes that may be read ahead when seeking isn't supported, -1 for unlimited", OFFSET(read_ahead_limit), AV_OPT_TYPE_INT, { .i64 = 65536 }, -1, INT_MAX, D },
+ {NULL},
+};
+
+static const AVClass cache_context_class = {
+ .class_name = "Cache",
+ .item_name = av_default_item_name,
+ .option = options,
+ .version = LIBAVUTIL_VERSION_INT,
+};
+
+const URLProtocol ff_cache_protocol = {
+ .name = "cache",
+ .url_open2 = cache_open,
+ .url_read = cache_read,
+ .url_seek = cache_seek,
+ .url_close = cache_close,
+ .priv_data_size = sizeof(Context),
+ .priv_data_class = &cache_context_class,
+};
diff --cc libavformat/concat.c
index 288bf14,a338df6..46b520f
--- a/libavformat/concat.c
+++ b/libavformat/concat.c
@@@ -97,9 -94,8 +97,9 @@@ static av_cold int concat_open(URLConte
uri += len + strspn(uri + len, AV_CAT_SEPARATOR);
/* creating URLContext */
- if ((err = ffurl_open(&uc, node_uri, flags,
- &h->interrupt_callback, NULL, h->protocols, h)) < 0)
+ err = ffurl_open_whitelist(&uc, node_uri, flags,
- &h->interrupt_callback, NULL, h->protocol_whitelist, h->protocol_blacklist);
++ &h->interrupt_callback, NULL, h->protocol_whitelist, h->protocol_blacklist, h);
+ if (err < 0)
break;
/* creating size */
diff --cc libavformat/crypto.c
index 4222e1e,55430c4..2999f50
--- a/libavformat/crypto.c
+++ b/libavformat/crypto.c
@@@ -117,55 -72,28 +117,55 @@@ static int crypto_open2(URLContext *h,
goto err;
}
- if (c->keylen < BLOCKSIZE || c->ivlen < BLOCKSIZE) {
- av_log(h, AV_LOG_ERROR, "Key or IV not set\n");
- ret = AVERROR(EINVAL);
- goto err;
+ if (flags & AVIO_FLAG_READ) {
+ if ((ret = set_aes_arg(c, &c->decrypt_key, &c->decrypt_keylen,
+ c->key, c->keylen, "decryption key")) < 0)
+ goto err;
+ if ((ret = set_aes_arg(c, &c->decrypt_iv, &c->decrypt_ivlen,
+ c->iv, c->ivlen, "decryption IV")) < 0)
+ goto err;
}
+
if (flags & AVIO_FLAG_WRITE) {
- av_log(h, AV_LOG_ERROR, "Only decryption is supported currently\n");
- ret = AVERROR(ENOSYS);
- goto err;
+ if ((ret = set_aes_arg(c, &c->encrypt_key, &c->encrypt_keylen,
+ c->key, c->keylen, "encryption key")) < 0)
+ if (ret < 0)
+ goto err;
+ if ((ret = set_aes_arg(c, &c->encrypt_iv, &c->encrypt_ivlen,
+ c->iv, c->ivlen, "encryption IV")) < 0)
+ goto err;
}
- if ((ret = ffurl_open(&c->hd, nested_url, AVIO_FLAG_READ,
- &h->interrupt_callback, NULL, h->protocols, h)) < 0) {
- av_log(h, AV_LOG_ERROR, "Unable to open input\n");
+
+ if ((ret = ffurl_open_whitelist(&c->hd, nested_url, flags,
+ &h->interrupt_callback, options,
- h->protocol_whitelist, h->protocol_blacklist)) < 0) {
++ h->protocol_whitelist, h->protocol_blacklist, h)) < 0) {
+ av_log(h, AV_LOG_ERROR, "Unable to open resource: %s\n", nested_url);
goto err;
}
- c->aes = av_aes_alloc();
- if (!c->aes) {
- ret = AVERROR(ENOMEM);
- goto err;
+
+ if (flags & AVIO_FLAG_READ) {
+ c->aes_decrypt = av_aes_alloc();
+ if (!c->aes_decrypt) {
+ ret = AVERROR(ENOMEM);
+ goto err;
+ }
+ ret = av_aes_init(c->aes_decrypt, c->decrypt_key, BLOCKSIZE*8, 1);
+ if (ret < 0)
+ goto err;
}
- av_aes_init(c->aes, c->key, 128, 1);
+ if (flags & AVIO_FLAG_WRITE) {
+ c->aes_encrypt = av_aes_alloc();
+ if (!c->aes_encrypt) {
+ ret = AVERROR(ENOMEM);
+ goto err;
+ }
+ ret = av_aes_init(c->aes_encrypt, c->encrypt_key, BLOCKSIZE*8, 0);
+ if (ret < 0)
+ goto err;
+ }
+
+ c->pad_len = 0;
h->is_streamed = 1;
diff --cc libavformat/ftp.c
index b9fee24,0000000..0663b47
mode 100644,000000..100644
--- a/libavformat/ftp.c
+++ b/libavformat/ftp.c
@@@ -1,1124 -1,0 +1,1124 @@@
+/*
+ * Copyright (c) 2013 Lukasz Marek <lukasz.m.luki at gmail.com>
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "libavutil/avstring.h"
+#include "libavutil/internal.h"
+#include "libavutil/parseutils.h"
+#include "avformat.h"
+#include "internal.h"
+#include "url.h"
+#include "libavutil/opt.h"
+#include "libavutil/bprint.h"
+
+#define CONTROL_BUFFER_SIZE 1024
+#define DIR_BUFFER_SIZE 4096
+
+typedef enum {
+ UNKNOWN,
+ READY,
+ DOWNLOADING,
+ UPLOADING,
+ LISTING_DIR,
+ DISCONNECTED
+} FTPState;
+
+typedef enum {
+ UNKNOWN_METHOD,
+ NLST,
+ MLSD
+} FTPListingMethod;
+
+typedef struct {
+ const AVClass *class;
+ URLContext *conn_control; /**< Control connection */
+ URLContext *conn_data; /**< Data connection, NULL when not connected */
+ uint8_t control_buffer[CONTROL_BUFFER_SIZE]; /**< Control connection buffer */
+ uint8_t *control_buf_ptr, *control_buf_end;
+ int server_data_port; /**< Data connection port opened by server, -1 on error. */
+ int server_control_port; /**< Control connection port, default is 21 */
+ char *hostname; /**< Server address. */
+ char *user; /**< Server user */
+ char *password; /**< Server user's password */
+ char *path; /**< Path to resource on server. */
+ int64_t filesize; /**< Size of file on server, -1 on error. */
+ int64_t position; /**< Current position, calculated. */
+ int rw_timeout; /**< Network timeout. */
+ const char *anonymous_password; /**< Password to be used for anonymous user. An email should be used. */
+ int write_seekable; /**< Control seekability, 0 = disable, 1 = enable. */
+ FTPState state; /**< State of data connection */
+ FTPListingMethod listing_method; /**< Called listing method */
+ char *features; /**< List of server's features represented as raw response */
+ char *dir_buffer;
+ size_t dir_buffer_size;
+ size_t dir_buffer_offset;
+ int utf8;
+} FTPContext;
+
+#define OFFSET(x) offsetof(FTPContext, x)
+#define D AV_OPT_FLAG_DECODING_PARAM
+#define E AV_OPT_FLAG_ENCODING_PARAM
+static const AVOption options[] = {
+ {"timeout", "set timeout of socket I/O operations", OFFSET(rw_timeout), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, D|E },
+ {"ftp-write-seekable", "control seekability of connection during encoding", OFFSET(write_seekable), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, E },
+ {"ftp-anonymous-password", "password for anonymous login. E-mail address should be used.", OFFSET(anonymous_password), AV_OPT_TYPE_STRING, { 0 }, 0, 0, D|E },
+ {NULL}
+};
+
+static const AVClass ftp_context_class = {
+ .class_name = "ftp",
+ .item_name = av_default_item_name,
+ .option = options,
+ .version = LIBAVUTIL_VERSION_INT,
+};
+
+static int ftp_close(URLContext *h);
+
+static int ftp_getc(FTPContext *s)
+{
+ int len;
+ if (s->control_buf_ptr >= s->control_buf_end) {
+ len = ffurl_read(s->conn_control, s->control_buffer, CONTROL_BUFFER_SIZE);
+ if (len < 0) {
+ return len;
+ } else if (!len) {
+ return -1;
+ } else {
+ s->control_buf_ptr = s->control_buffer;
+ s->control_buf_end = s->control_buffer + len;
+ }
+ }
+ return *s->control_buf_ptr++;
+}
+
+static int ftp_get_line(FTPContext *s, char *line, int line_size)
+{
+ int ch;
+ char *q = line;
+
+ for (;;) {
+ ch = ftp_getc(s);
+ if (ch < 0) {
+ return ch;
+ }
+ if (ch == '\n') {
+ /* process line */
+ if (q > line && q[-1] == '\r')
+ q--;
+ *q = '\0';
+ return 0;
+ } else {
+ if ((q - line) < line_size - 1)
+ *q++ = ch;
+ }
+ }
+}
+
+/*
+ * This routine returns ftp server response code.
+ * Server may send more than one response for a certain command.
+ * First expected code is returned.
+ */
+static int ftp_status(FTPContext *s, char **line, const int response_codes[])
+{
+ int err, i, dash = 0, result = 0, code_found = 0, linesize;
+ char buf[CONTROL_BUFFER_SIZE];
+ AVBPrint line_buffer;
+
+ if (line)
+ av_bprint_init(&line_buffer, 0, AV_BPRINT_SIZE_AUTOMATIC);
+
+ while (!code_found || dash) {
+ if ((err = ftp_get_line(s, buf, sizeof(buf))) < 0) {
+ if (line)
+ av_bprint_finalize(&line_buffer, NULL);
+ return err;
+ }
+
+ av_log(s, AV_LOG_DEBUG, "%s\n", buf);
+
+ linesize = strlen(buf);
+ err = 0;
+ if (linesize >= 3) {
+ for (i = 0; i < 3; ++i) {
+ if (buf[i] < '0' || buf[i] > '9') {
+ err = 0;
+ break;
+ }
+ err *= 10;
+ err += buf[i] - '0';
+ }
+ }
+
+ if (!code_found) {
+ if (err >= 500) {
+ code_found = 1;
+ result = err;
+ } else
+ for (i = 0; response_codes[i]; ++i) {
+ if (err == response_codes[i]) {
+ code_found = 1;
+ result = err;
+ break;
+ }
+ }
+ }
+ if (code_found) {
+ if (line)
+ av_bprintf(&line_buffer, "%s\r\n", buf);
+ if (linesize >= 4) {
+ if (!dash && buf[3] == '-')
+ dash = err;
+ else if (err == dash && buf[3] == ' ')
+ dash = 0;
+ }
+ }
+ }
+
+ if (line)
+ av_bprint_finalize(&line_buffer, line);
+ return result;
+}
+
+static int ftp_send_command(FTPContext *s, const char *command,
+ const int response_codes[], char **response)
+{
+ int err;
+
+ ff_dlog(s, "%s", command);
+
+ if (response)
+ *response = NULL;
+
+ if ((err = ffurl_write(s->conn_control, command, strlen(command))) < 0)
+ return err;
+ if (!err)
+ return -1;
+
+ /* return status */
+ if (response_codes) {
+ return ftp_status(s, response, response_codes);
+ }
+ return 0;
+}
+
+static void ftp_close_data_connection(FTPContext *s)
+{
+ ffurl_closep(&s->conn_data);
+ s->position = 0;
+ s->state = DISCONNECTED;
+}
+
+static void ftp_close_both_connections(FTPContext *s)
+{
+ ffurl_closep(&s->conn_control);
+ ftp_close_data_connection(s);
+}
+
+static int ftp_auth(FTPContext *s)
+{
+ char buf[CONTROL_BUFFER_SIZE];
+ int err;
+ static const int user_codes[] = {331, 230, 0};
+ static const int pass_codes[] = {230, 0};
+
+ snprintf(buf, sizeof(buf), "USER %s\r\n", s->user);
+ err = ftp_send_command(s, buf, user_codes, NULL);
+ if (err == 331) {
+ if (s->password) {
+ snprintf(buf, sizeof(buf), "PASS %s\r\n", s->password);
+ err = ftp_send_command(s, buf, pass_codes, NULL);
+ } else
+ return AVERROR(EACCES);
+ }
+ if (err != 230)
+ return AVERROR(EACCES);
+
+ return 0;
+}
+
+static int ftp_passive_mode_epsv(FTPContext *s)
+{
+ char *res = NULL, *start = NULL, *end = NULL;
+ int i;
+ static const char d = '|';
+ static const char *command = "EPSV\r\n";
+ static const int epsv_codes[] = {229, 0};
+
+ if (ftp_send_command(s, command, epsv_codes, &res) != 229 || !res)
+ goto fail;
+
+ for (i = 0; res[i]; ++i) {
+ if (res[i] == '(') {
+ start = res + i + 1;
+ } else if (res[i] == ')') {
+ end = res + i;
+ break;
+ }
+ }
+ if (!start || !end)
+ goto fail;
+
+ *end = '\0';
+ if (strlen(start) < 5)
+ goto fail;
+ if (start[0] != d || start[1] != d || start[2] != d || end[-1] != d)
+ goto fail;
+ start += 3;
+ end[-1] = '\0';
+
+ s->server_data_port = atoi(start);
+ ff_dlog(s, "Server data port: %d\n", s->server_data_port);
+
+ av_free(res);
+ return 0;
+
+ fail:
+ av_free(res);
+ s->server_data_port = -1;
+ return AVERROR(ENOSYS);
+}
+
+static int ftp_passive_mode(FTPContext *s)
+{
+ char *res = NULL, *start = NULL, *end = NULL;
+ int i;
+ static const char *command = "PASV\r\n";
+ static const int pasv_codes[] = {227, 0};
+
+ if (ftp_send_command(s, command, pasv_codes, &res) != 227 || !res)
+ goto fail;
+
+ for (i = 0; res[i]; ++i) {
+ if (res[i] == '(') {
+ start = res + i + 1;
+ } else if (res[i] == ')') {
+ end = res + i;
+ break;
+ }
+ }
+ if (!start || !end)
+ goto fail;
+
+ *end = '\0';
+ /* skip ip */
+ if (!av_strtok(start, ",", &end)) goto fail;
+ if (!av_strtok(end, ",", &end)) goto fail;
+ if (!av_strtok(end, ",", &end)) goto fail;
+ if (!av_strtok(end, ",", &end)) goto fail;
+
+ /* parse port number */
+ start = av_strtok(end, ",", &end);
+ if (!start) goto fail;
+ s->server_data_port = atoi(start) * 256;
+ start = av_strtok(end, ",", &end);
+ if (!start) goto fail;
+ s->server_data_port += atoi(start);
+ ff_dlog(s, "Server data port: %d\n", s->server_data_port);
+
+ av_free(res);
+ return 0;
+
+ fail:
+ av_free(res);
+ s->server_data_port = -1;
+ return AVERROR(EIO);
+}
+
+static int ftp_current_dir(FTPContext *s)
+{
+ char *res = NULL, *start = NULL, *end = NULL;
+ int i;
+ static const char *command = "PWD\r\n";
+ static const int pwd_codes[] = {257, 0};
+
+ if (ftp_send_command(s, command, pwd_codes, &res) != 257 || !res)
+ goto fail;
+
+ for (i = 0; res[i]; ++i) {
+ if (res[i] == '"') {
+ if (!start) {
+ start = res + i + 1;
+ continue;
+ }
+ end = res + i;
+ break;
+ }
+ }
+
+ if (!end)
+ goto fail;
+
+ *end = '\0';
+ s->path = av_strdup(start);
+
+ av_free(res);
+
+ if (!s->path)
+ return AVERROR(ENOMEM);
+ return 0;
+
+ fail:
+ av_free(res);
+ return AVERROR(EIO);
+}
+
+static int ftp_file_size(FTPContext *s)
+{
+ char command[CONTROL_BUFFER_SIZE];
+ char *res = NULL;
+ static const int size_codes[] = {213, 0};
+
+ snprintf(command, sizeof(command), "SIZE %s\r\n", s->path);
+ if (ftp_send_command(s, command, size_codes, &res) == 213 && res) {
+ s->filesize = strtoll(&res[4], NULL, 10);
+ } else {
+ s->filesize = -1;
+ av_free(res);
+ return AVERROR(EIO);
+ }
+
+ av_free(res);
+ return 0;
+}
+
+static int ftp_retrieve(FTPContext *s)
+{
+ char command[CONTROL_BUFFER_SIZE];
+ static const int retr_codes[] = {150, 125, 0};
+ int resp_code;
+
+ snprintf(command, sizeof(command), "RETR %s\r\n", s->path);
+ resp_code = ftp_send_command(s, command, retr_codes, NULL);
+ if (resp_code != 125 && resp_code != 150)
+ return AVERROR(EIO);
+
+ s->state = DOWNLOADING;
+
+ return 0;
+}
+
+static int ftp_store(FTPContext *s)
+{
+ char command[CONTROL_BUFFER_SIZE];
+ static const int stor_codes[] = {150, 125, 0};
+ int resp_code;
+
+ snprintf(command, sizeof(command), "STOR %s\r\n", s->path);
+ resp_code = ftp_send_command(s, command, stor_codes, NULL);
+ if (resp_code != 125 && resp_code != 150)
+ return AVERROR(EIO);
+
+ s->state = UPLOADING;
+
+ return 0;
+}
+
+static int ftp_type(FTPContext *s)
+{
+ static const char *command = "TYPE I\r\n";
+ static const int type_codes[] = {200, 0};
+
+ if (ftp_send_command(s, command, type_codes, NULL) != 200)
+ return AVERROR(EIO);
+
+ return 0;
+}
+
+static int ftp_restart(FTPContext *s, int64_t pos)
+{
+ char command[CONTROL_BUFFER_SIZE];
+ static const int rest_codes[] = {350, 0};
+
+ snprintf(command, sizeof(command), "REST %"PRId64"\r\n", pos);
+ if (ftp_send_command(s, command, rest_codes, NULL) != 350)
+ return AVERROR(EIO);
+
+ return 0;
+}
+
+static int ftp_set_dir(FTPContext *s)
+{
+ static const int cwd_codes[] = {250, 550, 0}; /* 550 is incorrect code */
+ char command[MAX_URL_SIZE];
+
+ snprintf(command, sizeof(command), "CWD %s\r\n", s->path);
+ if (ftp_send_command(s, command, cwd_codes, NULL) != 250)
+ return AVERROR(EIO);
+ return 0;
+}
+
+static int ftp_list_mlsd(FTPContext *s)
+{
+ static const char *command = "MLSD\r\n";
+ static const int mlsd_codes[] = {150, 500, 0}; /* 500 is incorrect code */
+
+ if (ftp_send_command(s, command, mlsd_codes, NULL) != 150)
+ return AVERROR(ENOSYS);
+ s->listing_method = MLSD;
+ return 0;
+}
+
+static int ftp_list_nlst(FTPContext *s)
+{
+ static const char *command = "NLST\r\n";
+ static const int nlst_codes[] = {226, 425, 426, 451, 450, 550, 0};
+
+ if (ftp_send_command(s, command, nlst_codes, NULL) != 226)
+ return AVERROR(ENOSYS);
+ s->listing_method = NLST;
+ return 0;
+}
+
+static int ftp_has_feature(FTPContext *s, const char *feature_name);
+
+static int ftp_list(FTPContext *s)
+{
+ int ret;
+ s->state = LISTING_DIR;
+
+ if ((ret = ftp_list_mlsd(s)) < 0)
+ ret = ftp_list_nlst(s);
+
+ return ret;
+}
+
+static int ftp_has_feature(FTPContext *s, const char *feature_name)
+{
+ if (!s->features)
+ return 0;
+
+ return av_stristr(s->features, feature_name) != NULL;
+}
+
+static int ftp_features(FTPContext *s)
+{
+ static const char *feat_command = "FEAT\r\n";
+ static const char *enable_utf8_command = "OPTS UTF8 ON\r\n";
+ static const int feat_codes[] = {211, 0};
+ static const int opts_codes[] = {200, 451, 0};
+
+ av_freep(&s->features);
+ if (ftp_send_command(s, feat_command, feat_codes, &s->features) != 211) {
+ av_freep(&s->features);
+ }
+
+ if (ftp_has_feature(s, "UTF8")) {
+ if (ftp_send_command(s, enable_utf8_command, opts_codes, NULL) == 200)
+ s->utf8 = 1;
+ }
+
+ return 0;
+}
+
+static int ftp_connect_control_connection(URLContext *h)
+{
+ char buf[CONTROL_BUFFER_SIZE], *response = NULL;
+ int err;
+ AVDictionary *opts = NULL;
+ FTPContext *s = h->priv_data;
+ static const int connect_codes[] = {220, 0};
+
+ if (!s->conn_control) {
+ ff_url_join(buf, sizeof(buf), "tcp", NULL,
+ s->hostname, s->server_control_port, NULL);
+ if (s->rw_timeout != -1) {
+ av_dict_set_int(&opts, "timeout", s->rw_timeout, 0);
+ } /* if option is not given, don't pass it and let tcp use its own default */
+ err = ffurl_open_whitelist(&s->conn_control, buf, AVIO_FLAG_READ_WRITE,
+ &h->interrupt_callback, &opts,
- h->protocol_whitelist, h->protocol_blacklist);
++ h->protocol_whitelist, h->protocol_blacklist, h);
+ av_dict_free(&opts);
+ if (err < 0) {
+ av_log(h, AV_LOG_ERROR, "Cannot open control connection\n");
+ return err;
+ }
+
+ /* check if server is ready */
+ if (ftp_status(s, ((h->flags & AVIO_FLAG_WRITE) ? &response : NULL), connect_codes) != 220) {
+ av_log(h, AV_LOG_ERROR, "FTP server not ready for new users\n");
+ return AVERROR(EACCES);
+ }
+
+ if ((h->flags & AVIO_FLAG_WRITE) && av_stristr(response, "pure-ftpd")) {
+ av_log(h, AV_LOG_WARNING, "Pure-FTPd server is used as an output protocol. It is known issue this implementation may produce incorrect content and it cannot be fixed at this moment.");
+ }
+ av_free(response);
+
+ if ((err = ftp_auth(s)) < 0) {
+ av_log(h, AV_LOG_ERROR, "FTP authentication failed\n");
+ return err;
+ }
+
+ if ((err = ftp_type(s)) < 0) {
+ av_log(h, AV_LOG_ERROR, "Set content type failed\n");
+ return err;
+ }
+
+ ftp_features(s);
+ }
+ return 0;
+}
+
+static int ftp_connect_data_connection(URLContext *h)
+{
+ int err;
+ char buf[CONTROL_BUFFER_SIZE];
+ AVDictionary *opts = NULL;
+ FTPContext *s = h->priv_data;
+
+ if (!s->conn_data) {
+ /* Enter passive mode */
+ if (ftp_passive_mode_epsv(s) < 0) {
+ /* Use PASV as fallback */
+ if ((err = ftp_passive_mode(s)) < 0)
+ return err;
+ }
+ /* Open data connection */
+ ff_url_join(buf, sizeof(buf), "tcp", NULL, s->hostname, s->server_data_port, NULL);
+ if (s->rw_timeout != -1) {
+ av_dict_set_int(&opts, "timeout", s->rw_timeout, 0);
+ } /* if option is not given, don't pass it and let tcp use its own default */
+ err = ffurl_open_whitelist(&s->conn_data, buf, h->flags,
+ &h->interrupt_callback, &opts,
- h->protocol_whitelist, h->protocol_blacklist);
++ h->protocol_whitelist, h->protocol_blacklist, h);
+ av_dict_free(&opts);
+ if (err < 0)
+ return err;
+
+ if (s->position)
+ if ((err = ftp_restart(s, s->position)) < 0)
+ return err;
+ }
+ s->state = READY;
+ return 0;
+}
+
+static int ftp_abort(URLContext *h)
+{
+ static const char *command = "ABOR\r\n";
+ int err;
+ static const int abor_codes[] = {225, 226, 0};
+ FTPContext *s = h->priv_data;
+
+ /* According to RCF 959:
+ "ABOR command tells the server to abort the previous FTP
+ service command and any associated transfer of data."
+
+ There are FTP server implementations that don't response
+ to any commands during data transfer in passive mode (including ABOR).
+
+ This implementation closes data connection by force.
+ */
+
+ if (ftp_send_command(s, command, NULL, NULL) < 0) {
+ ftp_close_both_connections(s);
+ if ((err = ftp_connect_control_connection(h)) < 0) {
+ av_log(h, AV_LOG_ERROR, "Reconnect failed.\n");
+ return err;
+ }
+ } else {
+ ftp_close_data_connection(s);
+ if (ftp_status(s, NULL, abor_codes) < 225) {
+ /* wu-ftpd also closes control connection after data connection closing */
+ ffurl_closep(&s->conn_control);
+ if ((err = ftp_connect_control_connection(h)) < 0) {
+ av_log(h, AV_LOG_ERROR, "Reconnect failed.\n");
+ return err;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int ftp_connect(URLContext *h, const char *url)
+{
+ char proto[10], path[MAX_URL_SIZE], credencials[MAX_URL_SIZE], hostname[MAX_URL_SIZE];
+ const char *tok_user = NULL, *tok_pass = NULL;
+ char *end = NULL, *newpath = NULL;
+ int err;
+ FTPContext *s = h->priv_data;
+
+ s->state = DISCONNECTED;
+ s->listing_method = UNKNOWN_METHOD;
+ s->filesize = -1;
+ s->position = 0;
+ s->features = NULL;
+
+ av_url_split(proto, sizeof(proto),
+ credencials, sizeof(credencials),
+ hostname, sizeof(hostname),
+ &s->server_control_port,
+ path, sizeof(path),
+ url);
+
+ tok_user = av_strtok(credencials, ":", &end);
+ tok_pass = av_strtok(end, ":", &end);
+ if (!tok_user) {
+ tok_user = "anonymous";
+ tok_pass = av_x_if_null(s->anonymous_password, "nopassword");
+ }
+ s->user = av_strdup(tok_user);
+ s->password = av_strdup(tok_pass);
+ s->hostname = av_strdup(hostname);
+ if (!s->hostname || !s->user || (tok_pass && !s->password)) {
+ return AVERROR(ENOMEM);
+ }
+
+ if (s->server_control_port < 0 || s->server_control_port > 65535)
+ s->server_control_port = 21;
+
+ if ((err = ftp_connect_control_connection(h)) < 0)
+ return err;
+
+ if ((err = ftp_current_dir(s)) < 0)
+ return err;
+
+ newpath = av_append_path_component(s->path, path);
+ if (!newpath)
+ return AVERROR(ENOMEM);
+ av_free(s->path);
+ s->path = newpath;
+
+ return 0;
+}
+
+static int ftp_open(URLContext *h, const char *url, int flags)
+{
+ FTPContext *s = h->priv_data;
+ int err;
+
+ ff_dlog(h, "ftp protocol open\n");
+
+ if ((err = ftp_connect(h, url)) < 0)
+ goto fail;
+
+ 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;
+ if (s->write_seekable != 1 && flags & AVIO_FLAG_WRITE)
+ h->is_streamed = 1;
+ }
+
+ return 0;
+
+ fail:
+ av_log(h, AV_LOG_ERROR, "FTP open failed\n");
+ ftp_close(h);
+ return err;
+}
+
+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;
+
+ ff_dlog(h, "ftp protocol seek %"PRId64" %d\n", pos, whence);
+
+ switch(whence) {
+ case AVSEEK_SIZE:
+ return s->filesize;
+ case SEEK_SET:
+ new_pos = pos;
+ break;
+ case SEEK_CUR:
+ new_pos = s->position + pos;
+ break;
+ case SEEK_END:
+ if (s->filesize < 0)
+ return AVERROR(EIO);
+ new_pos = s->filesize + pos;
+ break;
+ default:
+ return AVERROR(EINVAL);
+ }
+
+ if (h->is_streamed)
+ return AVERROR(EIO);
+
+ if (new_pos < 0) {
+ av_log(h, AV_LOG_ERROR, "Seeking to nagative position.\n");
+ return AVERROR(EINVAL);
+ }
+
+ fake_pos = s->filesize != -1 ? FFMIN(new_pos, s->filesize) : new_pos;
+ if (fake_pos != s->position) {
+ if ((err = ftp_abort(h)) < 0)
+ return err;
+ s->position = fake_pos;
+ }
+ 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;
+
+ ff_dlog(h, "ftp protocol read %d bytes\n", size);
+ retry:
+ if (s->state == DISCONNECTED) {
+ /* optimization */
+ if (s->position >= s->filesize)
+ return 0;
+ if ((err = ftp_connect_data_connection(h)) < 0)
+ return err;
+ }
+ if (s->state == READY) {
+ if (s->position >= s->filesize)
+ return 0;
+ if ((err = ftp_retrieve(s)) < 0)
+ return err;
+ }
+ 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) {
+ /* 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;
+ }
+ }
+ if (read <= 0 && s->position < s->filesize && !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;
+ }
+ }
+ 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)
+{
+ int err;
+ FTPContext *s = h->priv_data;
+ int written;
+
+ ff_dlog(h, "ftp protocol write %d bytes\n", size);
+
+ if (s->state == DISCONNECTED) {
+ if ((err = ftp_connect_data_connection(h)) < 0)
+ return err;
+ }
+ if (s->state == READY) {
+ if ((err = ftp_store(s)) < 0)
+ return err;
+ }
+ 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;
+
+ ff_dlog(h, "ftp protocol close\n");
+
+ ftp_close_both_connections(s);
+ av_freep(&s->user);
+ av_freep(&s->password);
+ av_freep(&s->hostname);
+ av_freep(&s->path);
+ av_freep(&s->features);
+
+ return 0;
+}
+
+static int ftp_get_file_handle(URLContext *h)
+{
+ FTPContext *s = h->priv_data;
+
+ ff_dlog(h, "ftp protocol get_file_handle\n");
+
+ if (s->conn_data)
+ return ffurl_get_file_handle(s->conn_data);
+
+ return AVERROR(EIO);
+}
+
+static int ftp_shutdown(URLContext *h, int flags)
+{
+ FTPContext *s = h->priv_data;
+
+ ff_dlog(h, "ftp protocol shutdown\n");
+
+ if (s->conn_data)
+ return ffurl_shutdown(s->conn_data, flags);
+
+ return AVERROR(EIO);
+}
+
+static int ftp_open_dir(URLContext *h)
+{
+ FTPContext *s = h->priv_data;
+ int ret;
+
+ if ((ret = ftp_connect(h, h->filename)) < 0)
+ goto fail;
+ if ((ret = ftp_set_dir(s)) < 0)
+ goto fail;
+ if ((ret = ftp_connect_data_connection(h)) < 0)
+ goto fail;
+ if ((ret = ftp_list(s)) < 0)
+ goto fail;
+ s->dir_buffer = av_malloc(DIR_BUFFER_SIZE);
+ if (!s->dir_buffer) {
+ ret = AVERROR(ENOMEM);
+ goto fail;
+ }
+ s->dir_buffer[0] = 0;
+ if (s->conn_data && s->state == LISTING_DIR)
+ return 0;
+ fail:
+ ffurl_closep(&s->conn_control);
+ ffurl_closep(&s->conn_data);
+ return ret;
+}
+
+static int64_t ftp_parse_date(const char *date)
+{
+ struct tm tv;
+ memset(&tv, 0, sizeof(struct tm));
+ av_small_strptime(date, "%Y%m%d%H%M%S", &tv);
+ return INT64_C(1000000) * av_timegm(&tv);
+}
+
+static int ftp_parse_entry_nlst(char *line, AVIODirEntry *next)
+{
+ next->name = av_strdup(line);
+ return 0;
+}
+
+static int ftp_parse_entry_mlsd(char *mlsd, AVIODirEntry *next)
+{
+ char *fact, *value;
+ ff_dlog(NULL, "%s\n", mlsd);
+ while(fact = av_strtok(mlsd, ";", &mlsd)) {
+ if (fact[0] == ' ') {
+ next->name = av_strdup(&fact[1]);
+ continue;
+ }
+ fact = av_strtok(fact, "=", &value);
+ if (!av_strcasecmp(fact, "type")) {
+ if (!av_strcasecmp(value, "cdir") || !av_strcasecmp(value, "pdir"))
+ return 1;
+ if (!av_strcasecmp(value, "dir"))
+ next->type = AVIO_ENTRY_DIRECTORY;
+ else if (!av_strcasecmp(value, "file"))
+ next->type = AVIO_ENTRY_FILE;
+ else if (!av_strcasecmp(value, "OS.unix=slink:"))
+ next->type = AVIO_ENTRY_SYMBOLIC_LINK;
+ } else if (!av_strcasecmp(fact, "modify")) {
+ next->modification_timestamp = ftp_parse_date(value);
+ } else if (!av_strcasecmp(fact, "UNIX.mode")) {
+ next->filemode = strtoumax(value, NULL, 8);
+ } else if (!av_strcasecmp(fact, "UNIX.uid") || !av_strcasecmp(fact, "UNIX.owner"))
+ next->user_id = strtoumax(value, NULL, 10);
+ else if (!av_strcasecmp(fact, "UNIX.gid") || !av_strcasecmp(fact, "UNIX.group"))
+ next->group_id = strtoumax(value, NULL, 10);
+ else if (!av_strcasecmp(fact, "size") || !av_strcasecmp(fact, "sizd"))
+ next->size = strtoll(value, NULL, 10);
+ }
+ return 0;
+}
+
+/**
+ * @return 0 on success, negative on error, positive on entry to discard.
+ */
+static int ftp_parse_entry(URLContext *h, char *line, AVIODirEntry *next)
+{
+ FTPContext *s = h->priv_data;
+
+ switch (s->listing_method) {
+ case MLSD:
+ return ftp_parse_entry_mlsd(line, next);
+ case NLST:
+ return ftp_parse_entry_nlst(line, next);
+ case UNKNOWN_METHOD:
+ default:
+ return -1;
+ }
+}
+
+static int ftp_read_dir(URLContext *h, AVIODirEntry **next)
+{
+ FTPContext *s = h->priv_data;
+ char *start, *found;
+ int ret, retried;
+
+ do {
+ retried = 0;
+ start = s->dir_buffer + s->dir_buffer_offset;
+ while (!(found = strstr(start, "\n"))) {
+ if (retried)
+ return AVERROR(EIO);
+ s->dir_buffer_size -= s->dir_buffer_offset;
+ s->dir_buffer_offset = 0;
+ if (s->dir_buffer_size)
+ memmove(s->dir_buffer, start, s->dir_buffer_size);
+ ret = ffurl_read(s->conn_data, s->dir_buffer + s->dir_buffer_size, DIR_BUFFER_SIZE - (s->dir_buffer_size + 1));
+ if (ret < 0)
+ return ret;
+ if (!ret) {
+ *next = NULL;
+ return 0;
+ }
+ s->dir_buffer_size += ret;
+ s->dir_buffer[s->dir_buffer_size] = 0;
+ start = s->dir_buffer;
+ retried = 1;
+ }
+ s->dir_buffer_offset += (found + 1 - start);
+ found[0] = 0;
+ if (found > start && found[-1] == '\r')
+ found[-1] = 0;
+
+ *next = ff_alloc_dir_entry();
+ if (!*next)
+ return AVERROR(ENOMEM);
+ (*next)->utf8 = s->utf8;
+ ret = ftp_parse_entry(h, start, *next);
+ if (ret) {
+ avio_free_directory_entry(next);
+ if (ret < 0)
+ return ret;
+ }
+ } while (ret > 0);
+ return 0;
+}
+
+static int ftp_close_dir(URLContext *h)
+{
+ FTPContext *s = h->priv_data;
+ av_freep(&s->dir_buffer);
+ ffurl_closep(&s->conn_control);
+ ffurl_closep(&s->conn_data);
+ return 0;
+}
+
+static int ftp_delete(URLContext *h)
+{
+ FTPContext *s = h->priv_data;
+ char command[MAX_URL_SIZE];
+ static const int del_codes[] = {250, 421, 450, 500, 501, 502, 530, 550, 0};
+ static const int rmd_codes[] = {250, 421, 500, 501, 502, 530, 550, 0};
+ int ret;
+
+ if ((ret = ftp_connect(h, h->filename)) < 0)
+ goto cleanup;
+
+ snprintf(command, sizeof(command), "DELE %s\r\n", s->path);
+ if (ftp_send_command(s, command, del_codes, NULL) == 250) {
+ ret = 0;
+ goto cleanup;
+ }
+
+ snprintf(command, sizeof(command), "RMD %s\r\n", s->path);
+ if (ftp_send_command(s, command, rmd_codes, NULL) == 250)
+ ret = 0;
+ else
+ ret = AVERROR(EIO);
+
+cleanup:
+ ftp_close(h);
+ return ret;
+}
+
+static int ftp_move(URLContext *h_src, URLContext *h_dst)
+{
+ FTPContext *s = h_src->priv_data;
+ char command[MAX_URL_SIZE], path[MAX_URL_SIZE];
+ static const int rnfr_codes[] = {350, 421, 450, 500, 501, 502, 503, 530, 0};
+ static const int rnto_codes[] = {250, 421, 500, 501, 502, 503, 530, 532, 553, 0};
+ int ret;
+
+ if ((ret = ftp_connect(h_src, h_src->filename)) < 0)
+ goto cleanup;
+
+ snprintf(command, sizeof(command), "RNFR %s\r\n", s->path);
+ if (ftp_send_command(s, command, rnfr_codes, NULL) != 350) {
+ ret = AVERROR(EIO);
+ goto cleanup;
+ }
+
+ av_url_split(0, 0, 0, 0, 0, 0, 0,
+ path, sizeof(path),
+ h_dst->filename);
+ snprintf(command, sizeof(command), "RNTO %s\r\n", path);
+ if (ftp_send_command(s, command, rnto_codes, NULL) == 250)
+ ret = 0;
+ else
+ ret = AVERROR(EIO);
+
+cleanup:
+ ftp_close(h_src);
+ return ret;
+}
+
+const URLProtocol ff_ftp_protocol = {
+ .name = "ftp",
+ .url_open = ftp_open,
+ .url_read = ftp_read,
+ .url_write = ftp_write,
+ .url_seek = ftp_seek,
+ .url_close = ftp_close,
+ .url_get_file_handle = ftp_get_file_handle,
+ .url_shutdown = ftp_shutdown,
+ .priv_data_size = sizeof(FTPContext),
+ .priv_data_class = &ftp_context_class,
+ .url_open_dir = ftp_open_dir,
+ .url_read_dir = ftp_read_dir,
+ .url_close_dir = ftp_close_dir,
+ .url_delete = ftp_delete,
+ .url_move = ftp_move,
+ .flags = URL_PROTOCOL_FLAG_NETWORK,
+ .default_whitelist = "tcp",
+};
diff --cc libavformat/gopher.c
index d1113e7,6d9fc38..3070b24
--- a/libavformat/gopher.c
+++ b/libavformat/gopher.c
@@@ -93,8 -93,8 +93,8 @@@ static int gopher_open(URLContext *h, c
ff_url_join(buf, sizeof(buf), "tcp", NULL, hostname, port, NULL);
s->hd = NULL;
- err = ffurl_open(&s->hd, buf, AVIO_FLAG_READ_WRITE,
- &h->interrupt_callback, NULL, h->protocols, h);
+ err = ffurl_open_whitelist(&s->hd, buf, AVIO_FLAG_READ_WRITE,
- &h->interrupt_callback, NULL, h->protocol_whitelist, h->protocol_blacklist);
++ &h->interrupt_callback, NULL, h->protocol_whitelist, h->protocol_blacklist, h);
if (err < 0)
goto fail;
diff --cc libavformat/hlsproto.c
index 097e520,4c3048a..2b19ed0
--- a/libavformat/hlsproto.c
+++ b/libavformat/hlsproto.c
@@@ -305,9 -303,8 +305,9 @@@ retry
}
url = s->segments[s->cur_seq_no - s->start_seq_no]->url,
av_log(h, AV_LOG_DEBUG, "opening %s\n", url);
- ret = ffurl_open(&s->seg_hd, url, AVIO_FLAG_READ,
- &h->interrupt_callback, NULL, h->protocols, h);
+ ret = ffurl_open_whitelist(&s->seg_hd, url, AVIO_FLAG_READ,
+ &h->interrupt_callback, NULL,
- h->protocol_whitelist, h->protocol_blacklist);
++ h->protocol_whitelist, h->protocol_blacklist, h);
if (ret < 0) {
if (ff_check_interrupt(&h->interrupt_callback))
return AVERROR_EXIT;
diff --cc libavformat/http.c
index 814ca01,8fe8d11..622814b
--- a/libavformat/http.c
+++ b/libavformat/http.c
@@@ -219,9 -180,8 +219,9 @@@ static int http_open_cnx_internal(URLCo
ff_url_join(buf, sizeof(buf), lower_proto, NULL, hostname, port, NULL);
if (!s->hd) {
- err = ffurl_open(&s->hd, buf, AVIO_FLAG_READ_WRITE,
- &h->interrupt_callback, options, h->protocols, h);
+ err = ffurl_open_whitelist(&s->hd, buf, AVIO_FLAG_READ_WRITE,
+ &h->interrupt_callback, options,
- h->protocol_whitelist, h->protocol_blacklist);
++ h->protocol_whitelist, h->protocol_blacklist, h);
if (err < 0)
return err;
}
@@@ -309,166 -270,6 +309,166 @@@ int ff_http_do_new_request(URLContext *
return ret;
}
+int ff_http_averror(int status_code, int default_averror)
+{
+ switch (status_code) {
+ case 400: return AVERROR_HTTP_BAD_REQUEST;
+ case 401: return AVERROR_HTTP_UNAUTHORIZED;
+ case 403: return AVERROR_HTTP_FORBIDDEN;
+ case 404: return AVERROR_HTTP_NOT_FOUND;
+ default: break;
+ }
+ if (status_code >= 400 && status_code <= 499)
+ return AVERROR_HTTP_OTHER_4XX;
+ else if (status_code >= 500)
+ return AVERROR_HTTP_SERVER_ERROR;
+ else
+ return default_averror;
+}
+
+static int http_write_reply(URLContext* h, int status_code)
+{
+ int ret, body = 0, reply_code, message_len;
+ const char *reply_text, *content_type;
+ HTTPContext *s = h->priv_data;
+ char message[BUFFER_SIZE];
+ content_type = "text/plain";
+
+ if (status_code < 0)
+ body = 1;
+ switch (status_code) {
+ case AVERROR_HTTP_BAD_REQUEST:
+ case 400:
+ reply_code = 400;
+ reply_text = "Bad Request";
+ break;
+ case AVERROR_HTTP_FORBIDDEN:
+ case 403:
+ reply_code = 403;
+ reply_text = "Forbidden";
+ break;
+ case AVERROR_HTTP_NOT_FOUND:
+ case 404:
+ reply_code = 404;
+ reply_text = "Not Found";
+ break;
+ case 200:
+ reply_code = 200;
+ reply_text = "OK";
+ content_type = "application/octet-stream";
+ break;
+ case AVERROR_HTTP_SERVER_ERROR:
+ case 500:
+ reply_code = 500;
+ reply_text = "Internal server error";
+ break;
+ default:
+ return AVERROR(EINVAL);
+ }
+ if (body) {
+ s->chunked_post = 0;
+ message_len = snprintf(message, sizeof(message),
+ "HTTP/1.1 %03d %s\r\n"
+ "Content-Type: %s\r\n"
+ "Content-Length: %"SIZE_SPECIFIER"\r\n"
+ "\r\n"
+ "%03d %s\r\n",
+ reply_code,
+ reply_text,
+ content_type,
+ strlen(reply_text) + 6, // 3 digit status code + space + \r\n
+ reply_code,
+ reply_text);
+ } else {
+ s->chunked_post = 1;
+ message_len = snprintf(message, sizeof(message),
+ "HTTP/1.1 %03d %s\r\n"
+ "Content-Type: %s\r\n"
+ "Transfer-Encoding: chunked\r\n"
+ "\r\n",
+ reply_code,
+ reply_text,
+ content_type);
+ }
+ av_log(h, AV_LOG_TRACE, "HTTP reply header: \n%s----\n", message);
+ if ((ret = ffurl_write(s->hd, message, message_len)) < 0)
+ return ret;
+ return 0;
+}
+
+static void handle_http_errors(URLContext *h, int error)
+{
+ av_assert0(error < 0);
+ http_write_reply(h, error);
+}
+
+static int http_handshake(URLContext *c)
+{
+ int ret, err, new_location;
+ HTTPContext *ch = c->priv_data;
+ URLContext *cl = ch->hd;
+ switch (ch->handshake_step) {
+ case LOWER_PROTO:
+ av_log(c, AV_LOG_TRACE, "Lower protocol\n");
+ if ((ret = ffurl_handshake(cl)) > 0)
+ return 2 + ret;
+ if (ret < 0)
+ return ret;
+ ch->handshake_step = READ_HEADERS;
+ ch->is_connected_server = 1;
+ return 2;
+ case READ_HEADERS:
+ av_log(c, AV_LOG_TRACE, "Read headers\n");
+ if ((err = http_read_header(c, &new_location)) < 0) {
+ handle_http_errors(c, err);
+ return err;
+ }
+ ch->handshake_step = WRITE_REPLY_HEADERS;
+ return 1;
+ case WRITE_REPLY_HEADERS:
+ av_log(c, AV_LOG_TRACE, "Reply code: %d\n", ch->reply_code);
+ if ((err = http_write_reply(c, ch->reply_code)) < 0)
+ return err;
+ ch->handshake_step = FINISH;
+ return 1;
+ case FINISH:
+ return 0;
+ }
+ // this should never be reached.
+ return AVERROR(EINVAL);
+}
+
+static int http_listen(URLContext *h, const char *uri, int flags,
+ AVDictionary **options) {
+ HTTPContext *s = h->priv_data;
+ int ret;
+ char hostname[1024], proto[10];
+ char lower_url[100];
+ const char *lower_proto = "tcp";
+ int port;
+ av_url_split(proto, sizeof(proto), NULL, 0, hostname, sizeof(hostname), &port,
+ NULL, 0, uri);
+ if (!strcmp(proto, "https"))
+ lower_proto = "tls";
+ ff_url_join(lower_url, sizeof(lower_url), lower_proto, NULL, hostname, port,
+ NULL);
+ if ((ret = av_dict_set_int(options, "listen", s->listen, 0)) < 0)
+ goto fail;
+ if ((ret = ffurl_open_whitelist(&s->hd, lower_url, AVIO_FLAG_READ_WRITE,
+ &h->interrupt_callback, options,
- h->protocol_whitelist, h->protocol_blacklist
++ h->protocol_whitelist, h->protocol_blacklist, h
+ )) < 0)
+ goto fail;
+ s->handshake_step = LOWER_PROTO;
+ if (s->listen == HTTP_SINGLE) { /* single client */
+ s->reply_code = 200;
+ while ((ret = http_handshake(h)) > 0);
+ }
+fail:
+ av_dict_free(&s->chained_options);
+ return ret;
+}
+
static int http_open(URLContext *h, const char *uri, int flags,
AVDictionary **options)
{
@@@ -1580,9 -1078,8 +1580,9 @@@ static int http_proxy_open(URLContext *
ff_url_join(lower_url, sizeof(lower_url), "tcp", NULL, hostname, port,
NULL);
redo:
- ret = ffurl_open(&s->hd, lower_url, AVIO_FLAG_READ_WRITE,
- &h->interrupt_callback, NULL, h->protocols, h);
+ ret = ffurl_open_whitelist(&s->hd, lower_url, AVIO_FLAG_READ_WRITE,
+ &h->interrupt_callback, NULL,
- h->protocol_whitelist, h->protocol_blacklist);
++ h->protocol_whitelist, h->protocol_blacklist, h);
if (ret < 0)
return ret;
diff --cc libavformat/icecast.c
index f7ca5cb,820681b..02e3e38
--- a/libavformat/icecast.c
+++ b/libavformat/icecast.c
@@@ -164,10 -176,11 +164,10 @@@ static int icecast_open(URLContext *h,
// Build new URI for passing to http protocol
ff_url_join(h_url, sizeof(h_url), "http", auth, host, port, "%s", path);
// Finally open http proto handler
- ret = ffurl_open(&s->hd, h_url, AVIO_FLAG_READ_WRITE, NULL, &opt_dict,
- h->protocols, h);
+ ret = ffurl_open_whitelist(&s->hd, h_url, AVIO_FLAG_READ_WRITE, NULL,
- &opt_dict, h->protocol_whitelist, h->protocol_blacklist);
++ &opt_dict, h->protocol_whitelist, h->protocol_blacklist, h);
cleanup:
- // Free variables
av_freep(&user);
av_freep(&headers);
av_dict_free(&opt_dict);
diff --cc libavformat/md5proto.c
index 2df34c6,08cdd71..0e04b90
--- a/libavformat/md5proto.c
+++ b/libavformat/md5proto.c
@@@ -69,9 -69,9 +69,9 @@@ static int md5_close(URLContext *h
av_strstart(filename, "md5:", &filename);
if (*filename) {
- err = ffurl_open(&out, filename, AVIO_FLAG_WRITE,
- &h->interrupt_callback, NULL,
- h->protocols, h);
+ err = ffurl_open_whitelist(&out, filename, AVIO_FLAG_WRITE,
+ &h->interrupt_callback, NULL,
- h->protocol_whitelist, h->protocol_blacklist);
++ h->protocol_whitelist, h->protocol_blacklist, h);
if (err)
return err;
err = ffurl_write(out, buf, i*2+1);
diff --cc libavformat/mmst.c
index 0f48b46,1489076..1d13959
--- a/libavformat/mmst.c
+++ b/libavformat/mmst.c
@@@ -528,9 -519,8 +528,9 @@@ static int mms_open(URLContext *h, cons
// establish tcp connection.
ff_url_join(tcpname, sizeof(tcpname), "tcp", NULL, mmst->host, port, NULL);
- err = ffurl_open(&mms->mms_hd, tcpname, AVIO_FLAG_READ_WRITE,
- &h->interrupt_callback, NULL, h->protocols, h);
+ err = ffurl_open_whitelist(&mms->mms_hd, tcpname, AVIO_FLAG_READ_WRITE,
+ &h->interrupt_callback, NULL,
- h->protocol_whitelist, h->protocol_blacklist);
++ h->protocol_whitelist, h->protocol_blacklist, h);
if (err)
goto fail;
diff --cc libavformat/rtmpcrypt.c
index 3d5eb22,cf121c6..c41ae43
--- a/libavformat/rtmpcrypt.c
+++ b/libavformat/rtmpcrypt.c
@@@ -264,9 -264,8 +264,9 @@@ static int rtmpe_open(URLContext *h, co
}
/* open the tcp or ffrtmphttp connection */
- if ((ret = ffurl_open(&rt->stream, url, AVIO_FLAG_READ_WRITE,
- &h->interrupt_callback, NULL, h->protocols, h)) < 0) {
+ if ((ret = ffurl_open_whitelist(&rt->stream, url, AVIO_FLAG_READ_WRITE,
+ &h->interrupt_callback, NULL,
- h->protocol_whitelist, h->protocol_blacklist)) < 0) {
++ h->protocol_whitelist, h->protocol_blacklist, h)) < 0) {
rtmpe_close(h);
return ret;
}
diff --cc libavformat/rtmpproto.c
index c01bc40,d4f35f6..18d915a
--- a/libavformat/rtmpproto.c
+++ b/libavformat/rtmpproto.c
@@@ -1118,9 -1118,8 +1118,9 @@@ static int rtmp_calc_swfhash(URLContex
int ret = 0;
/* Get the SWF player file. */
- if ((ret = ffurl_open(&stream, rt->swfverify, AVIO_FLAG_READ,
- &s->interrupt_callback, NULL, s->protocols, s)) < 0) {
+ if ((ret = ffurl_open_whitelist(&stream, rt->swfverify, AVIO_FLAG_READ,
+ &s->interrupt_callback, NULL,
- s->protocol_whitelist, s->protocol_blacklist)) < 0) {
++ s->protocol_whitelist, s->protocol_blacklist, s)) < 0) {
av_log(s, AV_LOG_ERROR, "Cannot open connection %s.\n", rt->swfverify);
goto fail;
}
@@@ -2648,9 -2640,8 +2648,9 @@@ static int rtmp_open(URLContext *s, con
}
reconnect:
- if ((ret = ffurl_open(&rt->stream, buf, AVIO_FLAG_READ_WRITE,
- &s->interrupt_callback, &opts, s->protocols, s)) < 0) {
+ if ((ret = ffurl_open_whitelist(&rt->stream, buf, AVIO_FLAG_READ_WRITE,
+ &s->interrupt_callback, &opts,
- s->protocol_whitelist, s->protocol_blacklist)) < 0) {
++ s->protocol_whitelist, s->protocol_blacklist, s)) < 0) {
av_log(s , AV_LOG_ERROR, "Cannot open connection %s\n", buf);
goto fail;
}
diff --cc libavformat/rtpproto.c
index 2062083,6582e4a..fa1dcb5
--- a/libavformat/rtpproto.c
+++ b/libavformat/rtpproto.c
@@@ -377,40 -366,19 +377,40 @@@ static int rtp_open(URLContext *h, cons
}
}
- build_udp_url(s, buf, sizeof(buf),
- hostname, rtp_port, s->local_rtpport, sources, block);
- if (ffurl_open(&s->rtp_hd, buf, flags, &h->interrupt_callback, NULL,
- h->protocols, h) < 0)
- goto fail;
- if (s->local_rtpport >= 0 && s->local_rtcpport < 0)
- s->local_rtcpport = ff_udp_get_local_port(s->rtp_hd) + 1;
-
- build_udp_url(s, buf, sizeof(buf),
- hostname, s->rtcp_port, s->local_rtcpport, sources, block);
- if (ffurl_open(&s->rtcp_hd, buf, flags, &h->interrupt_callback, NULL,
- h->protocols, h) < 0)
- goto fail;
+ for (i = 0; i < max_retry_count; i++) {
+ build_udp_url(s, buf, sizeof(buf),
+ hostname, rtp_port, s->local_rtpport,
+ sources, block);
+ if (ffurl_open_whitelist(&s->rtp_hd, buf, flags, &h->interrupt_callback,
- NULL, h->protocol_whitelist, h->protocol_blacklist) < 0)
++ NULL, h->protocol_whitelist, h->protocol_blacklist, h) < 0)
+ goto fail;
+ s->local_rtpport = ff_udp_get_local_port(s->rtp_hd);
+ if(s->local_rtpport == 65535) {
+ s->local_rtpport = -1;
+ continue;
+ }
+ rtcpflags = flags | AVIO_FLAG_WRITE;
+ if (s->local_rtcpport < 0) {
+ s->local_rtcpport = s->local_rtpport + 1;
+ build_udp_url(s, buf, sizeof(buf),
+ hostname, s->rtcp_port, s->local_rtcpport,
+ sources, block);
+ if (ffurl_open_whitelist(&s->rtcp_hd, buf, rtcpflags,
+ &h->interrupt_callback, NULL,
- h->protocol_whitelist, h->protocol_blacklist) < 0) {
++ h->protocol_whitelist, h->protocol_blacklist, h) < 0) {
+ s->local_rtpport = s->local_rtcpport = -1;
+ continue;
+ }
+ break;
+ }
+ build_udp_url(s, buf, sizeof(buf),
+ hostname, s->rtcp_port, s->local_rtcpport,
+ sources, block);
+ if (ffurl_open_whitelist(&s->rtcp_hd, buf, rtcpflags, &h->interrupt_callback,
- NULL, h->protocol_whitelist, h->protocol_blacklist) < 0)
++ NULL, h->protocol_whitelist, h->protocol_blacklist, h) < 0)
+ goto fail;
+ break;
+ }
/* just to ease handle access. XXX: need to suppress direct handle
access */
diff --cc libavformat/rtsp.c
index bbff70b,7e430e8..6888a2b
--- a/libavformat/rtsp.c
+++ b/libavformat/rtsp.c
@@@ -1468,8 -1465,8 +1468,8 @@@ int ff_rtsp_make_setup_request(AVFormat
"?localport=%d", j);
/* we will use two ports per rtp stream (rtp and rtcp) */
j += 2;
- err = ffurl_open(&rtsp_st->rtp_handle, buf, AVIO_FLAG_READ_WRITE,
- &s->interrupt_callback, &opts, rt->protocols, NULL);
+ err = ffurl_open_whitelist(&rtsp_st->rtp_handle, buf, AVIO_FLAG_READ_WRITE,
- &s->interrupt_callback, &opts, s->protocol_whitelist, s->protocol_blacklist);
++ &s->interrupt_callback, &opts, s->protocol_whitelist, s->protocol_blacklist, NULL);
av_dict_free(&opts);
@@@ -1611,8 -1609,8 +1611,8 @@@
namebuf, sizeof(namebuf), NULL, 0, NI_NUMERICHOST);
ff_url_join(url, sizeof(url), "rtp", NULL, namebuf,
port, "%s", optbuf);
- if (ffurl_open(&rtsp_st->rtp_handle, url, AVIO_FLAG_READ_WRITE,
- &s->interrupt_callback, NULL, rt->protocols, NULL) < 0) {
+ if (ffurl_open_whitelist(&rtsp_st->rtp_handle, url, AVIO_FLAG_READ_WRITE,
- &s->interrupt_callback, NULL, s->protocol_whitelist, s->protocol_blacklist) < 0) {
++ &s->interrupt_callback, NULL, s->protocol_whitelist, s->protocol_blacklist, NULL) < 0) {
err = AVERROR_INVALIDDATA;
goto fail;
}
@@@ -1795,14 -1800,12 +1795,14 @@@ redirect
goto fail;
}
} else {
+ int ret;
/* open the tcp connection */
ff_url_join(tcpname, sizeof(tcpname), lower_rtsp_proto, NULL,
- host, port, NULL);
- if (ffurl_open(&rt->rtsp_hd, tcpname, AVIO_FLAG_READ_WRITE,
- &s->interrupt_callback, NULL, rt->protocols, NULL) < 0) {
- err = AVERROR(EIO);
+ host, port,
+ "?timeout=%d", rt->stimeout);
+ if ((ret = ffurl_open_whitelist(&rt->rtsp_hd, tcpname, AVIO_FLAG_READ_WRITE,
- &s->interrupt_callback, NULL, s->protocol_whitelist, s->protocol_blacklist)) < 0) {
++ &s->interrupt_callback, NULL, s->protocol_whitelist, s->protocol_blacklist, NULL)) < 0) {
+ err = ret;
goto fail;
}
rt->rtsp_hd_out = rt->rtsp_hd;
@@@ -2316,8 -2310,8 +2316,8 @@@ static int sdp_read_header(AVFormatCont
append_source_addrs(url, sizeof(url), "block",
rtsp_st->nb_exclude_source_addrs,
rtsp_st->exclude_source_addrs);
- err = ffurl_open(&rtsp_st->rtp_handle, url, AVIO_FLAG_READ_WRITE,
- &s->interrupt_callback, &opts, rt->protocols, NULL);
+ err = ffurl_open_whitelist(&rtsp_st->rtp_handle, url, AVIO_FLAG_READ,
- &s->interrupt_callback, &opts, s->protocol_whitelist, s->protocol_blacklist);
++ &s->interrupt_callback, &opts, s->protocol_whitelist, s->protocol_blacklist, NULL);
av_dict_free(&opts);
@@@ -2386,8 -2380,15 +2386,8 @@@ static int rtp_read_header(AVFormatCont
if (!ff_network_init())
return AVERROR(EIO);
- if (!rt->protocols) {
- rt->protocols = ffurl_get_protocols(s->protocol_whitelist,
- s->protocol_blacklist);
- if (!rt->protocols)
- return AVERROR(ENOMEM);
- }
-
- ret = ffurl_open(&in, s->filename, AVIO_FLAG_READ,
- &s->interrupt_callback, NULL, rt->protocols, NULL);
+ ret = ffurl_open_whitelist(&in, s->filename, AVIO_FLAG_READ,
- &s->interrupt_callback, NULL, s->protocol_whitelist, s->protocol_blacklist);
++ &s->interrupt_callback, NULL, s->protocol_whitelist, s->protocol_blacklist, NULL);
if (ret)
goto fail;
diff --cc libavformat/rtspdec.c
index ca7acc8,03374dc..a722b98
--- a/libavformat/rtspdec.c
+++ b/libavformat/rtspdec.c
@@@ -294,9 -294,8 +294,9 @@@ static int rtsp_read_setup(AVFormatCont
av_dict_set(&opts, "buffer_size", buf, 0);
ff_url_join(url, sizeof(url), "rtp", NULL, host, localport, NULL);
av_log(s, AV_LOG_TRACE, "Opening: %s", url);
- ret = ffurl_open(&rtsp_st->rtp_handle, url, AVIO_FLAG_READ_WRITE,
- &s->interrupt_callback, &opts, rt->protocols, NULL);
+ ret = ffurl_open_whitelist(&rtsp_st->rtp_handle, url, AVIO_FLAG_READ_WRITE,
+ &s->interrupt_callback, &opts,
- s->protocol_whitelist, s->protocol_blacklist);
++ s->protocol_whitelist, s->protocol_blacklist, NULL);
av_dict_free(&opts);
if (ret)
localport += 2;
@@@ -663,9 -666,8 +663,9 @@@ static int rtsp_listen(AVFormatContext
ff_url_join(tcpname, sizeof(tcpname), lower_proto, NULL, host, port,
"?listen&listen_timeout=%d", rt->initial_timeout * 1000);
- if (ret = ffurl_open(&rt->rtsp_hd, tcpname, AVIO_FLAG_READ_WRITE,
- &s->interrupt_callback, NULL, rt->protocols, NULL)) {
+ if (ret = ffurl_open_whitelist(&rt->rtsp_hd, tcpname, AVIO_FLAG_READ_WRITE,
+ &s->interrupt_callback, NULL,
- s->protocol_whitelist, s->protocol_blacklist)) {
++ s->protocol_whitelist, s->protocol_blacklist, NULL)) {
av_log(s, AV_LOG_ERROR, "Unable to open RTSP for listening\n");
return ret;
}
diff --cc libavformat/sapdec.c
index 218c32a,ce0ceaa..522b38d
--- a/libavformat/sapdec.c
+++ b/libavformat/sapdec.c
@@@ -83,11 -85,17 +83,11 @@@ static int sap_read_header(AVFormatCont
av_strlcpy(host, "224.2.127.254", sizeof(host));
}
- sap->protocols = ffurl_get_protocols(s->protocol_whitelist,
- s->protocol_blacklist);
- if (!sap->protocols) {
- ret = AVERROR(ENOMEM);
- goto fail;
- }
-
ff_url_join(url, sizeof(url), "udp", NULL, host, port, "?localport=%d",
port);
- ret = ffurl_open(&sap->ann_fd, url, AVIO_FLAG_READ,
- &s->interrupt_callback, NULL, sap->protocols, NULL);
+ ret = ffurl_open_whitelist(&sap->ann_fd, url, AVIO_FLAG_READ,
+ &s->interrupt_callback, NULL,
- s->protocol_whitelist, s->protocol_blacklist);
++ s->protocol_whitelist, s->protocol_blacklist, NULL);
if (ret)
goto fail;
diff --cc libavformat/sapenc.c
index 0699e3e,ed3a024..3098e34
--- a/libavformat/sapenc.c
+++ b/libavformat/sapenc.c
@@@ -149,9 -159,8 +149,9 @@@ static int sap_write_header(AVFormatCon
"?ttl=%d", ttl);
if (!same_port)
base_port += 2;
- ret = ffurl_open(&fd, url, AVIO_FLAG_WRITE, &s->interrupt_callback, NULL,
- sap->protocols, NULL);
+ ret = ffurl_open_whitelist(&fd, url, AVIO_FLAG_WRITE,
+ &s->interrupt_callback, NULL,
- s->protocol_whitelist, s->protocol_blacklist);
++ s->protocol_whitelist, s->protocol_blacklist, NULL);
if (ret) {
ret = AVERROR(EIO);
goto fail;
@@@ -169,9 -178,8 +169,9 @@@
ff_url_join(url, sizeof(url), "udp", NULL, announce_addr, port,
"?ttl=%d&connect=1", ttl);
- ret = ffurl_open(&sap->ann_fd, url, AVIO_FLAG_WRITE,
- &s->interrupt_callback, NULL, sap->protocols, NULL);
+ ret = ffurl_open_whitelist(&sap->ann_fd, url, AVIO_FLAG_WRITE,
+ &s->interrupt_callback, NULL,
- s->protocol_whitelist, s->protocol_blacklist);
++ s->protocol_whitelist, s->protocol_blacklist, NULL);
if (ret) {
ret = AVERROR(EIO);
goto fail;
diff --cc libavformat/smoothstreamingenc.c
index 193c360,bf1f8d4..dabd1ea
--- a/libavformat/smoothstreamingenc.c
+++ b/libavformat/smoothstreamingenc.c
@@@ -122,8 -125,8 +122,8 @@@ static int64_t ism_seek(void *opaque, i
AVDictionary *opts = NULL;
os->tail_out = os->out;
av_dict_set(&opts, "truncate", "0", 0);
- ret = ffurl_open(&os->out, frag->file, AVIO_FLAG_WRITE, &os->ctx->interrupt_callback, &opts,
- os->protocols, NULL);
+ ret = ffurl_open_whitelist(&os->out, frag->file, AVIO_FLAG_WRITE,
- &os->ctx->interrupt_callback, &opts, os->ctx->protocol_whitelist, os->ctx->protocol_blacklist);
++ &os->ctx->interrupt_callback, &opts, os->ctx->protocol_whitelist, os->ctx->protocol_blacklist, NULL);
av_dict_free(&opts);
if (ret < 0) {
os->out = os->tail_out;
@@@ -131,8 -134,8 +131,8 @@@
return ret;
}
av_dict_set(&opts, "truncate", "0", 0);
- ffurl_open(&os->out2, frag->infofile, AVIO_FLAG_WRITE, &os->ctx->interrupt_callback, &opts,
- os->protocols, NULL);
+ ffurl_open_whitelist(&os->out2, frag->infofile, AVIO_FLAG_WRITE,
- &os->ctx->interrupt_callback, &opts, os->ctx->protocol_whitelist, os->ctx->protocol_blacklist);
++ &os->ctx->interrupt_callback, &opts, os->ctx->protocol_whitelist, os->ctx->protocol_blacklist, NULL);
av_dict_free(&opts);
ffurl_seek(os->out, offset - frag->start_pos, SEEK_SET);
if (os->out2)
@@@ -526,7 -540,8 +526,7 @@@ static int ism_flush(AVFormatContext *s
continue;
snprintf(filename, sizeof(filename), "%s/temp", os->dirname);
- ret = ffurl_open_whitelist(&os->out, filename, AVIO_FLAG_WRITE, &s->interrupt_callback, NULL, s->protocol_whitelist, s->protocol_blacklist);
- ret = ffurl_open(&os->out, filename, AVIO_FLAG_WRITE, &s->interrupt_callback, NULL,
- c->protocols, NULL);
++ ret = ffurl_open_whitelist(&os->out, filename, AVIO_FLAG_WRITE, &s->interrupt_callback, NULL, s->protocol_whitelist, s->protocol_blacklist, NULL);
if (ret < 0)
break;
os->cur_start_pos = os->tail_pos;
diff --cc libavformat/srtpproto.c
index ef87c08,e6e035a..5e6e516
--- a/libavformat/srtpproto.c
+++ b/libavformat/srtpproto.c
@@@ -80,8 -80,8 +80,8 @@@ static int srtp_open(URLContext *h, con
av_url_split(NULL, 0, NULL, 0, hostname, sizeof(hostname), &rtp_port,
path, sizeof(path), uri);
ff_url_join(buf, sizeof(buf), "rtp", NULL, hostname, rtp_port, "%s", path);
- if ((ret = ffurl_open(&s->rtp_hd, buf, flags, &h->interrupt_callback, NULL,
- h->protocols, h)) < 0)
+ if ((ret = ffurl_open_whitelist(&s->rtp_hd, buf, flags, &h->interrupt_callback,
- NULL, h->protocol_whitelist, h->protocol_blacklist)) < 0)
++ NULL, h->protocol_whitelist, h->protocol_blacklist, h)) < 0)
goto fail;
h->max_packet_size = FFMIN(s->rtp_hd->max_packet_size,
diff --cc libavformat/subfile.c
index fdd328a,0000000..fa971e1
mode 100644,000000..100644
--- a/libavformat/subfile.c
+++ b/libavformat/subfile.c
@@@ -1,149 -1,0 +1,149 @@@
+/*
+ * Copyright (c) 2014 Nicolas George
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with FFmpeg; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "libavutil/avassert.h"
+#include "libavutil/avstring.h"
+#include "libavutil/opt.h"
+#include "avformat.h"
+#include "url.h"
+
+typedef struct SubfileContext {
+ const AVClass *class;
+ URLContext *h;
+ int64_t start;
+ int64_t end;
+ int64_t pos;
+} SubfileContext;
+
+#define OFFSET(field) offsetof(SubfileContext, field)
+#define D AV_OPT_FLAG_DECODING_PARAM
+
+static const AVOption subfile_options[] = {
+ { "start", "start offset", OFFSET(start), AV_OPT_TYPE_INT64, {.i64 = 0}, 0, INT64_MAX, D },
+ { "end", "end offset", OFFSET(end), AV_OPT_TYPE_INT64, {.i64 = 0}, 0, INT64_MAX, D },
+ { NULL }
+};
+
+#undef OFFSET
+#undef D
+
+static const AVClass subfile_class = {
+ .class_name = "subfile",
+ .item_name = av_default_item_name,
+ .option = subfile_options,
+ .version = LIBAVUTIL_VERSION_INT,
+};
+
+static int slave_seek(URLContext *h)
+{
+ SubfileContext *c = h->priv_data;
+ int64_t ret;
+
+ if ((ret = ffurl_seek(c->h, c->pos, SEEK_SET)) != c->pos) {
+ if (ret >= 0)
+ ret = AVERROR_BUG;
+ av_log(h, AV_LOG_ERROR, "Impossible to seek in file: %s\n",
+ av_err2str(ret));
+ return ret;
+ }
+ return 0;
+}
+
+static int subfile_open(URLContext *h, const char *filename, int flags,
+ AVDictionary **options)
+{
+ SubfileContext *c = h->priv_data;
+ int ret;
+
+ if (c->end <= c->start) {
+ av_log(h, AV_LOG_ERROR, "end before start\n");
+ return AVERROR(EINVAL);
+ }
+ av_strstart(filename, "subfile:", &filename);
+ ret = ffurl_open_whitelist(&c->h, filename, flags, &h->interrupt_callback,
- options, h->protocol_whitelist, h->protocol_blacklist);
++ options, h->protocol_whitelist, h->protocol_blacklist, h);
+ if (ret < 0)
+ return ret;
+ c->pos = c->start;
+ if ((ret = slave_seek(h)) < 0) {
+ ffurl_close(c->h);
+ return ret;
+ }
+ return 0;
+}
+
+static int subfile_close(URLContext *h)
+{
+ SubfileContext *c = h->priv_data;
+ return ffurl_close(c->h);
+}
+
+static int subfile_read(URLContext *h, unsigned char *buf, int size)
+{
+ SubfileContext *c = h->priv_data;
+ int64_t rest = c->end - c->pos;
+ int ret;
+
+ if (rest <= 0)
+ return 0;
+ size = FFMIN(size, rest);
+ ret = ffurl_read(c->h, buf, size);
+ if (ret >= 0)
+ c->pos += ret;
+ return ret;
+}
+
+static int64_t subfile_seek(URLContext *h, int64_t pos, int whence)
+{
+ SubfileContext *c = h->priv_data;
+ int64_t new_pos = -1;
+ int ret;
+
+ if (whence == AVSEEK_SIZE)
+ return c->end - c->start;
+ switch (whence) {
+ case SEEK_SET:
+ new_pos = c->start + pos;
+ break;
+ case SEEK_CUR:
+ new_pos += pos;
+ break;
+ case SEEK_END:
+ new_pos = c->end + c->pos;
+ break;
+ }
+ if (new_pos < c->start)
+ return AVERROR(EINVAL);
+ c->pos = new_pos;
+ if ((ret = slave_seek(h)) < 0)
+ return ret;
+ return c->pos - c->start;
+}
+
+const URLProtocol ff_subfile_protocol = {
+ .name = "subfile",
+ .url_open2 = subfile_open,
+ .url_read = subfile_read,
+ .url_seek = subfile_seek,
+ .url_close = subfile_close,
+ .priv_data_size = sizeof(SubfileContext),
+ .priv_data_class = &subfile_class,
+ .default_whitelist = "file",
+};
diff --cc libavformat/tls.c
index 2a59aa7,fab243e..10e0792
--- a/libavformat/tls.c
+++ b/libavformat/tls.c
@@@ -104,7 -75,6 +104,7 @@@ int ff_tls_open_underlying(TLSShared *c
proxy_port, "/%s", dest);
}
- return ffurl_open(&c->tcp, buf, AVIO_FLAG_READ_WRITE,
- &parent->interrupt_callback, options, parent->protocols, parent);
+ return ffurl_open_whitelist(&c->tcp, buf, AVIO_FLAG_READ_WRITE,
+ &parent->interrupt_callback, options,
- parent->protocol_whitelist, parent->protocol_blacklist);
++ parent->protocol_whitelist, parent->protocol_blacklist, parent);
}
diff --cc libavformat/url.h
index 4ce60cc,5cbfd1a..f19b91c
--- a/libavformat/url.h
+++ b/libavformat/url.h
@@@ -136,38 -134,17 +136,41 @@@ int ffurl_connect(URLContext *uc, AVDic
* @param options A dictionary filled with protocol-private options. On return
* this parameter will be destroyed and replaced with a dict containing options
* that were not found. May be NULL.
- * @param protocols a NULL-terminate list of protocols available for use by
- * this context and its children. The caller must ensure this
- * list remains valid until the context is closed.
+ * @param parent An enclosing URLContext, whose generic options should
+ * be applied to this URLContext as well.
- * @return 0 in case of success, a negative value corresponding to an
+ * @return >= 0 in case of success, a negative value corresponding to an
* AVERROR code in case of failure
*/
-int ffurl_open(URLContext **puc, const char *filename, int flags,
+int ffurl_open_whitelist(URLContext **puc, const char *filename, int flags,
const AVIOInterruptCB *int_cb, AVDictionary **options,
- const char *whitelist, const char* blacklist);
- const URLProtocol **protocols, URLContext *parent);
++ const char *whitelist, const char* blacklist,
++ URLContext *parent);
+
+int ffurl_open(URLContext **puc, const char *filename, int flags,
+ const AVIOInterruptCB *int_cb, AVDictionary **options);
+
+/**
+ * Accept an URLContext c on an URLContext s
+ *
+ * @param s server context
+ * @param c client context, must be unallocated.
+ * @return >= 0 on success, ff_neterrno() on failure.
+ */
+int ffurl_accept(URLContext *s, URLContext **c);
+
+/**
+ * Perform one step of the protocol handshake to accept a new client.
+ * See avio_handshake() for details.
+ * Implementations should try to return decreasing values.
+ * If the protocol uses an underlying protocol, the underlying handshake is
+ * usually the first step, and the return value can be:
+ * (largest value for this protocol) + (return value from other protocol)
+ *
+ * @param c the client context
+ * @return >= 0 on success or a negative value corresponding
+ * to an AVERROR code on failure
+ */
+int ffurl_handshake(URLContext *c);
/**
* Read up to size bytes from the resource accessed by h, and store
More information about the ffmpeg-cvslog
mailing list