diff --git a/libavformat/avio.c b/libavformat/avio.c index 4896782072..a990ea2229 100644 --- a/libavformat/avio.c +++ b/libavformat/avio.c @@ -23,6 +23,7 @@ #include "libavutil/dict.h" #include "libavutil/opt.h" #include "libavutil/time.h" +#include "libavutil/avassert.h" #include "os_support.h" #include "avformat.h" #if CONFIG_NETWORK @@ -418,6 +419,79 @@ int avio_check(const char *url, int flags) return ret; } +int avio_open_dir(AVIODirContext **s, const char *url, AVDictionary **options) +{ + URLContext *h = NULL; + AVIODirContext *ctx = NULL; + int ret; + av_assert0(s); + + ctx = av_mallocz(sizeof(*ctx)); + if (!ctx) { + ret = AVERROR(ENOMEM); + goto fail; + } + + if ((ret = ffurl_alloc(&h, url, AVIO_FLAG_READ, NULL)) < 0) + goto fail; + + if (h->prot->url_open_dir && h->prot->url_read_dir && h->prot->url_close_dir) { + if (options && h->prot->priv_data_class && + (ret = av_opt_set_dict(h->priv_data, options)) < 0) + goto fail; + ret = h->prot->url_open_dir(h); + } else + ret = AVERROR(ENOSYS); + if (ret < 0) + goto fail; + + ctx->url_context = h; + *s = ctx; + return 0; + + fail: + av_free(ctx); + *s = NULL; + ffurl_close(h); + return ret; +} + +int avio_read_dir(AVIODirContext *s, AVIODirEntry **next) +{ + URLContext *h; + int ret; + + if (!s || !s->url_context) + return AVERROR(EINVAL); + h = s->url_context; + if ((ret = h->prot->url_read_dir(h, next)) < 0) + avio_free_directory_entry(next); + return ret; +} + +int avio_close_dir(AVIODirContext **s) +{ + URLContext *h; + + av_assert0(s); + if (!(*s) || !(*s)->url_context) + return AVERROR(EINVAL); + h = (*s)->url_context; + h->prot->url_close_dir(h); + ffurl_close(h); + av_freep(s); + *s = NULL; + return 0; +} + +void avio_free_directory_entry(AVIODirEntry **entry) +{ + if (!entry || !*entry) + return; + av_free((*entry)->name); + av_freep(entry); +} + int64_t ffurl_size(URLContext *h) { int64_t pos, size; diff --git a/libavformat/avio.h b/libavformat/avio.h index 8fc7e27d4e..bbd482a52e 100644 --- a/libavformat/avio.h +++ b/libavformat/avio.h @@ -34,7 +34,6 @@ #include "libavformat/version.h" - #define AVIO_SEEKABLE_NORMAL 0x0001 /**< Seeking works like for a local file */ /** @@ -53,6 +52,47 @@ typedef struct AVIOInterruptCB { void *opaque; } AVIOInterruptCB; +/** + * Directory entry types. + */ +enum AVIODirEntryType { + AVIO_ENTRY_UNKNOWN, + AVIO_ENTRY_BLOCK_DEVICE, + AVIO_ENTRY_CHARACTER_DEVICE, + AVIO_ENTRY_DIRECTORY, + AVIO_ENTRY_NAMED_PIPE, + AVIO_ENTRY_SYMBOLIC_LINK, + AVIO_ENTRY_SOCKET, + AVIO_ENTRY_FILE +}; + +/** + * Describes single entry of the directory. + * + * Only name and type fields are guaranteed be set. + * Rest of fields are protocol or/and platform dependent and might be unknown. + */ +typedef struct AVIODirEntry { + char *name; /**< Filename */ + int type; /**< Type of the entry */ + int utf8; /**< Set to 1 when name is encoded with UTF-8, 0 otherwise. + Name can be encoded with UTF-8 eventhough 0 is set. */ + int64_t size; /**< File size in bytes, -1 if unknown. */ + int64_t modification_timestamp; /**< Time of last modification in microseconds since unix + epoch, -1 if unknown. */ + int64_t access_timestamp; /**< Time of last access in microseconds since unix epoch, + -1 if unknown. */ + int64_t status_change_timestamp; /**< Time of last status change in microseconds since unix + epoch, -1 if unknown. */ + int64_t user_id; /**< User ID of owner, -1 if unknown. */ + int64_t group_id; /**< Group ID of owner, -1 if unknown. */ + int64_t filemode; /**< Unix file mode, -1 if unknown. */ +} AVIODirEntry; + +typedef struct AVIODirContext { + struct URLContext *url_context; +} AVIODirContext; + /** * Bytestream IO Context. * New fields can be added to the end with minor version bumps. @@ -180,6 +220,48 @@ const char *avio_find_protocol_name(const char *url); */ int avio_check(const char *url, int flags); +/** + * Open directory for reading. + * + * @param s directory read context. Pointer to a NULL pointer must be passed. + * @param url directory to be listed. + * @param options A dictionary filled with protocol-private options. On return + * this parameter will be destroyed and replaced with a dictionary + * containing options that were not found. May be NULL. + * @return >=0 on success or negative on error. + */ +int avio_open_dir(AVIODirContext **s, const char *url, AVDictionary **options); + +/** + * Get next directory entry. + * + * Returned entry must be freed with avio_free_directory_entry(). In particular + * it may outlive AVIODirContext. + * + * @param s directory read context. + * @param[out] next next entry or NULL when no more entries. + * @return >=0 on success or negative on error. + */ +int avio_read_dir(AVIODirContext *s, AVIODirEntry **next); + +/** + * Close directory. + * + * @note Entries created using avio_read_dir() are not deleted and must be + * freeded with avio_free_directory_entry(). + * + * @param s directory read context. + * @return >=0 on success or negative on error. + */ +int avio_close_dir(AVIODirContext **s); + +/** + * Free entry allocated by avio_read_dir(). + * + * @param entry entry to be freed. + */ +void avio_free_directory_entry(AVIODirEntry **entry); + /** * Allocate and initialize an AVIOContext for buffered I/O. It must be later * freed with av_free(). diff --git a/libavformat/url.c b/libavformat/url.c index acfb0cf2f0..3a2c78bc52 100644 --- a/libavformat/url.c +++ b/libavformat/url.c @@ -145,3 +145,19 @@ void ff_make_absolute_url(char *buf, int size, const char *base, } av_strlcat(buf, rel, size); } + +AVIODirEntry *ff_alloc_dir_entry(void) +{ + AVIODirEntry *entry = av_mallocz(sizeof(AVIODirEntry)); + if (entry) { + entry->type = AVIO_ENTRY_UNKNOWN; + entry->size = -1; + entry->modification_timestamp = -1; + entry->access_timestamp = -1; + entry->status_change_timestamp = -1; + entry->user_id = -1; + entry->group_id = -1; + entry->filemode = -1; + } + return entry; +} diff --git a/libavformat/url.h b/libavformat/url.h index d0035f3617..1a845b77e7 100644 --- a/libavformat/url.h +++ b/libavformat/url.h @@ -87,6 +87,9 @@ typedef struct URLProtocol { const AVClass *priv_data_class; int flags; int (*url_check)(URLContext *h, int mask); + int (*url_open_dir)(URLContext *h); + int (*url_read_dir)(URLContext *h, AVIODirEntry **next); + int (*url_close_dir)(URLContext *h); } URLProtocol; /** @@ -280,5 +283,12 @@ int ff_url_join(char *str, int size, const char *proto, void ff_make_absolute_url(char *buf, int size, const char *base, const char *rel); +/** + * Allocate directory entry with default values. + * + * @return entry or NULL on error + */ +AVIODirEntry *ff_alloc_dir_entry(void); + #endif /* AVFORMAT_URL_H */