Merge commit 'df9f22d42b0905385629a9d368bb5a1eef2b45ef'
authorMichael Niedermayer <michaelni@gmx.at>
Sun, 16 Jun 2013 22:05:02 +0000 (00:05 +0200)
committerMichael Niedermayer <michaelni@gmx.at>
Sun, 16 Jun 2013 22:09:31 +0000 (00:09 +0200)
* commit 'df9f22d42b0905385629a9d368bb5a1eef2b45ef':
  avf: move url utility functions in a separate file

Conflicts:
libavformat/internal.h

Merged-by: Michael Niedermayer <michaelni@gmx.at>
1  2 
libavformat/Makefile
libavformat/concatdec.c
libavformat/internal.h
libavformat/url-test.c
libavformat/url.c
libavformat/url.h
libavformat/utils.c

Simple merge
index 79fb394,0000000..428c749
mode 100644,000000..100644
--- /dev/null
@@@ -1,407 -1,0 +1,408 @@@
 +/*
 + * Copyright (c) 2012 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/avstring.h"
 +#include "libavutil/opt.h"
 +#include "libavutil/parseutils.h"
 +#include "avformat.h"
 +#include "internal.h"
++#include "url.h"
 +
 +typedef struct {
 +    char *url;
 +    int64_t start_time;
 +    int64_t duration;
 +} ConcatFile;
 +
 +typedef struct {
 +    AVClass *class;
 +    ConcatFile *files;
 +    ConcatFile *cur_file;
 +    unsigned nb_files;
 +    AVFormatContext *avf;
 +    int safe;
 +    int seekable;
 +} ConcatContext;
 +
 +static int concat_probe(AVProbeData *probe)
 +{
 +    return memcmp(probe->buf, "ffconcat version 1.0", 20) ?
 +           0 : AVPROBE_SCORE_MAX;
 +}
 +
 +static char *get_keyword(uint8_t **cursor)
 +{
 +    char *ret = *cursor += strspn(*cursor, SPACE_CHARS);
 +    *cursor += strcspn(*cursor, SPACE_CHARS);
 +    if (**cursor) {
 +        *((*cursor)++) = 0;
 +        *cursor += strspn(*cursor, SPACE_CHARS);
 +    }
 +    return ret;
 +}
 +
 +static int safe_filename(const char *f)
 +{
 +    const char *start = f;
 +
 +    for (; *f; f++) {
 +        /* A-Za-z0-9_- */
 +        if (!((unsigned)((*f | 32) - 'a') < 26 ||
 +              (unsigned)(*f - '0') < 10 || *f == '_' || *f == '-')) {
 +            if (f == start)
 +                return 0;
 +            else if (*f == '/')
 +                start = f + 1;
 +            else if (*f != '.')
 +                return 0;
 +        }
 +    }
 +    return 1;
 +}
 +
 +#define FAIL(retcode) do { ret = (retcode); goto fail; } while(0)
 +
 +static int add_file(AVFormatContext *avf, char *filename, ConcatFile **rfile,
 +                    unsigned *nb_files_alloc)
 +{
 +    ConcatContext *cat = avf->priv_data;
 +    ConcatFile *file;
 +    char *url = NULL;
 +    size_t url_len;
 +    int ret;
 +
 +    if (cat->safe > 0 && !safe_filename(filename)) {
 +        av_log(avf, AV_LOG_ERROR, "Unsafe file name '%s'\n", filename);
 +        FAIL(AVERROR(EPERM));
 +    }
 +    url_len = strlen(avf->filename) + strlen(filename) + 16;
 +    if (!(url = av_malloc(url_len)))
 +        FAIL(AVERROR(ENOMEM));
 +    ff_make_absolute_url(url, url_len, avf->filename, filename);
 +    av_freep(&filename);
 +
 +    if (cat->nb_files >= *nb_files_alloc) {
 +        size_t n = FFMAX(*nb_files_alloc * 2, 16);
 +        ConcatFile *new_files;
 +        if (n <= cat->nb_files || n > SIZE_MAX / sizeof(*cat->files) ||
 +            !(new_files = av_realloc(cat->files, n * sizeof(*cat->files))))
 +            FAIL(AVERROR(ENOMEM));
 +        cat->files = new_files;
 +        *nb_files_alloc = n;
 +    }
 +
 +    file = &cat->files[cat->nb_files++];
 +    memset(file, 0, sizeof(*file));
 +    *rfile = file;
 +
 +    file->url        = url;
 +    file->start_time = AV_NOPTS_VALUE;
 +    file->duration   = AV_NOPTS_VALUE;
 +
 +    return 0;
 +
 +fail:
 +    av_free(url);
 +    av_free(filename);
 +    return ret;
 +}
 +
 +static int open_file(AVFormatContext *avf, unsigned fileno)
 +{
 +    ConcatContext *cat = avf->priv_data;
 +    ConcatFile *file = &cat->files[fileno];
 +    int ret;
 +
 +    if (cat->avf)
 +        avformat_close_input(&cat->avf);
 +    if ((ret = avformat_open_input(&cat->avf, file->url, NULL, NULL)) < 0 ||
 +        (ret = avformat_find_stream_info(cat->avf, NULL)) < 0) {
 +        av_log(avf, AV_LOG_ERROR, "Impossible to open '%s'\n", file->url);
 +        return ret;
 +    }
 +    cat->cur_file = file;
 +    if (file->start_time == AV_NOPTS_VALUE)
 +        file->start_time = !fileno ? 0 :
 +                           cat->files[fileno - 1].start_time +
 +                           cat->files[fileno - 1].duration;
 +    return 0;
 +}
 +
 +static int concat_read_close(AVFormatContext *avf)
 +{
 +    ConcatContext *cat = avf->priv_data;
 +    unsigned i;
 +
 +    if (cat->avf)
 +        avformat_close_input(&cat->avf);
 +    for (i = 0; i < cat->nb_files; i++)
 +        av_freep(&cat->files[i].url);
 +    av_freep(&cat->files);
 +    return 0;
 +}
 +
 +static int concat_read_header(AVFormatContext *avf)
 +{
 +    ConcatContext *cat = avf->priv_data;
 +    uint8_t buf[4096];
 +    uint8_t *cursor, *keyword;
 +    int ret, line = 0, i;
 +    unsigned nb_files_alloc = 0;
 +    ConcatFile *file = NULL;
 +    AVStream *st, *source_st;
 +    int64_t time = 0;
 +
 +    while (1) {
 +        if ((ret = ff_get_line(avf->pb, buf, sizeof(buf))) <= 0)
 +            break;
 +        line++;
 +        cursor = buf;
 +        keyword = get_keyword(&cursor);
 +        if (!*keyword || *keyword == '#')
 +            continue;
 +
 +        if (!strcmp(keyword, "file")) {
 +            char *filename = av_get_token((const char **)&cursor, SPACE_CHARS);
 +            if (!filename) {
 +                av_log(avf, AV_LOG_ERROR, "Line %d: filename required\n", line);
 +                FAIL(AVERROR_INVALIDDATA);
 +            }
 +            if ((ret = add_file(avf, filename, &file, &nb_files_alloc)) < 0)
 +                FAIL(ret);
 +        } else if (!strcmp(keyword, "duration")) {
 +            char *dur_str = get_keyword(&cursor);
 +            int64_t dur;
 +            if (!file) {
 +                av_log(avf, AV_LOG_ERROR, "Line %d: duration without file\n",
 +                       line);
 +                FAIL(AVERROR_INVALIDDATA);
 +            }
 +            if ((ret = av_parse_time(&dur, dur_str, 1)) < 0) {
 +                av_log(avf, AV_LOG_ERROR, "Line %d: invalid duration '%s'\n",
 +                       line, dur_str);
 +                FAIL(ret);
 +            }
 +            file->duration = dur;
 +        } else if (!strcmp(keyword, "ffconcat")) {
 +            char *ver_kw  = get_keyword(&cursor);
 +            char *ver_val = get_keyword(&cursor);
 +            if (strcmp(ver_kw, "version") || strcmp(ver_val, "1.0")) {
 +                av_log(avf, AV_LOG_ERROR, "Line %d: invalid version\n", line);
 +                FAIL(AVERROR_INVALIDDATA);
 +            }
 +            if (cat->safe < 0)
 +                cat->safe = 1;
 +        } else {
 +            av_log(avf, AV_LOG_ERROR, "Line %d: unknown keyword '%s'\n",
 +                   line, keyword);
 +            FAIL(AVERROR_INVALIDDATA);
 +        }
 +    }
 +    if (ret < 0)
 +        FAIL(ret);
 +    if (!cat->nb_files)
 +        FAIL(AVERROR_INVALIDDATA);
 +
 +    for (i = 0; i < cat->nb_files; i++) {
 +        if (cat->files[i].start_time == AV_NOPTS_VALUE)
 +            cat->files[i].start_time = time;
 +        else
 +            time = cat->files[i].start_time;
 +        if (cat->files[i].duration == AV_NOPTS_VALUE)
 +            break;
 +        time += cat->files[i].duration;
 +    }
 +    if (i == cat->nb_files) {
 +        avf->duration = time;
 +        cat->seekable = 1;
 +    }
 +
 +    if ((ret = open_file(avf, 0)) < 0)
 +        FAIL(ret);
 +    for (i = 0; i < cat->avf->nb_streams; i++) {
 +        if (!(st = avformat_new_stream(avf, NULL)))
 +            FAIL(AVERROR(ENOMEM));
 +        source_st = cat->avf->streams[i];
 +        if ((ret = avcodec_copy_context(st->codec, source_st->codec)) < 0)
 +            FAIL(ret);
 +        st->r_frame_rate        = source_st->r_frame_rate;
 +        st->avg_frame_rate      = source_st->avg_frame_rate;
 +        st->time_base           = source_st->time_base;
 +        st->sample_aspect_ratio = source_st->sample_aspect_ratio;
 +    }
 +
 +    return 0;
 +
 +fail:
 +    concat_read_close(avf);
 +    return ret;
 +}
 +
 +static int open_next_file(AVFormatContext *avf)
 +{
 +    ConcatContext *cat = avf->priv_data;
 +    unsigned fileno = cat->cur_file - cat->files;
 +
 +    if (cat->cur_file->duration == AV_NOPTS_VALUE)
 +        cat->cur_file->duration = cat->avf->duration;
 +
 +    if (++fileno >= cat->nb_files)
 +        return AVERROR_EOF;
 +    return open_file(avf, fileno);
 +}
 +
 +static int concat_read_packet(AVFormatContext *avf, AVPacket *pkt)
 +{
 +    ConcatContext *cat = avf->priv_data;
 +    int ret;
 +    int64_t delta;
 +
 +    while (1) {
 +        if ((ret = av_read_frame(cat->avf, pkt)) != AVERROR_EOF ||
 +            (ret = open_next_file(avf)) < 0)
 +            break;
 +    }
 +    delta = av_rescale_q(cat->cur_file->start_time - cat->avf->start_time,
 +                         AV_TIME_BASE_Q,
 +                         cat->avf->streams[pkt->stream_index]->time_base);
 +    if (pkt->pts != AV_NOPTS_VALUE)
 +        pkt->pts += delta;
 +    if (pkt->dts != AV_NOPTS_VALUE)
 +        pkt->dts += delta;
 +    return ret;
 +}
 +
 +static void rescale_interval(AVRational tb_in, AVRational tb_out,
 +                             int64_t *min_ts, int64_t *ts, int64_t *max_ts)
 +{
 +    *ts     = av_rescale_q    (*    ts, tb_in, tb_out);
 +    *min_ts = av_rescale_q_rnd(*min_ts, tb_in, tb_out,
 +                               AV_ROUND_UP   | AV_ROUND_PASS_MINMAX);
 +    *max_ts = av_rescale_q_rnd(*max_ts, tb_in, tb_out,
 +                               AV_ROUND_DOWN | AV_ROUND_PASS_MINMAX);
 +}
 +
 +static int try_seek(AVFormatContext *avf, int stream,
 +                    int64_t min_ts, int64_t ts, int64_t max_ts, int flags)
 +{
 +    ConcatContext *cat = avf->priv_data;
 +    int64_t t0 = cat->cur_file->start_time - cat->avf->start_time;
 +
 +    ts -= t0;
 +    min_ts = min_ts == INT64_MIN ? INT64_MIN : min_ts - t0;
 +    max_ts = max_ts == INT64_MAX ? INT64_MAX : max_ts - t0;
 +    if (stream >= 0) {
 +        if (stream >= cat->avf->nb_streams)
 +            return AVERROR(EIO);
 +        rescale_interval(AV_TIME_BASE_Q, cat->avf->streams[stream]->time_base,
 +                         &min_ts, &ts, &max_ts);
 +    }
 +    return avformat_seek_file(cat->avf, stream, min_ts, ts, max_ts, flags);
 +}
 +
 +static int real_seek(AVFormatContext *avf, int stream,
 +                     int64_t min_ts, int64_t ts, int64_t max_ts, int flags)
 +{
 +    ConcatContext *cat = avf->priv_data;
 +    int ret, left, right;
 +
 +    if (stream >= 0) {
 +        if (stream >= avf->nb_streams)
 +            return AVERROR(EINVAL);
 +        rescale_interval(avf->streams[stream]->time_base, AV_TIME_BASE_Q,
 +                         &min_ts, &ts, &max_ts);
 +    }
 +
 +    left  = 0;
 +    right = cat->nb_files;
 +    while (right - left > 1) {
 +        int mid = (left + right) / 2;
 +        if (ts < cat->files[mid].start_time)
 +            right = mid;
 +        else
 +            left  = mid;
 +    }
 +
 +    if ((ret = open_file(avf, left)) < 0)
 +        return ret;
 +
 +    ret = try_seek(avf, stream, min_ts, ts, max_ts, flags);
 +    if (ret < 0 &&
 +        left < cat->nb_files - 1 &&
 +        cat->files[left + 1].start_time < max_ts) {
 +        if ((ret = open_file(avf, left + 1)) < 0)
 +            return ret;
 +        ret = try_seek(avf, stream, min_ts, ts, max_ts, flags);
 +    }
 +    return ret;
 +}
 +
 +static int concat_seek(AVFormatContext *avf, int stream,
 +                       int64_t min_ts, int64_t ts, int64_t max_ts, int flags)
 +{
 +    ConcatContext *cat = avf->priv_data;
 +    ConcatFile *cur_file_saved = cat->cur_file;
 +    AVFormatContext *cur_avf_saved = cat->avf;
 +    int ret;
 +
 +    if (!cat->seekable)
 +        return AVERROR(ESPIPE); /* XXX: can we use it? */
 +    if (flags & (AVSEEK_FLAG_BYTE | AVSEEK_FLAG_FRAME))
 +        return AVERROR(ENOSYS);
 +    cat->avf = NULL;
 +    if ((ret = real_seek(avf, stream, min_ts, ts, max_ts, flags)) < 0) {
 +        if (cat->avf)
 +            avformat_close_input(&cat->avf);
 +        cat->avf      = cur_avf_saved;
 +        cat->cur_file = cur_file_saved;
 +    } else {
 +        avformat_close_input(&cur_avf_saved);
 +    }
 +    return ret;
 +}
 +
 +#define OFFSET(x) offsetof(ConcatContext, x)
 +#define DEC AV_OPT_FLAG_DECODING_PARAM
 +
 +static const AVOption options[] = {
 +    { "safe", "enable safe mode",
 +      OFFSET(safe), AV_OPT_TYPE_INT, {.i64 = -1}, -1, 1, DEC },
 +    { NULL }
 +};
 +
 +static const AVClass concat_class = {
 +    .class_name = "concat demuxer",
 +    .item_name  = av_default_item_name,
 +    .option     = options,
 +    .version    = LIBAVUTIL_VERSION_INT,
 +};
 +
 +
 +AVInputFormat ff_concat_demuxer = {
 +    .name           = "concat",
 +    .long_name      = NULL_IF_CONFIG_SMALL("Virtual concatenation script"),
 +    .priv_data_size = sizeof(ConcatContext),
 +    .read_probe     = concat_probe,
 +    .read_header    = concat_read_header,
 +    .read_packet    = concat_read_packet,
 +    .read_close     = concat_read_close,
 +    .read_seek2     = concat_seek,
 +    .priv_class     = &concat_class,
 +};
Simple merge
Simple merge
index 0000000,eeda1f0..47e1584
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,147 +1,147 @@@
 - * This file is part of Libav.
+ /*
+  * URL utility functions
+  * Copyright (c) 2000, 2001, 2002 Fabrice Bellard
+  *
 - * Libav is free software; you can redistribute it and/or
++ * This file is part of FFmpeg.
+  *
 - * Libav is distributed in the hope that it will be useful,
++ * 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.
+  *
 - * License along with Libav; if not, write to the Free Software
++ * 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 "avformat.h"
+ #include "config.h"
+ #include "url.h"
+ #if CONFIG_NETWORK
+ #include "network.h"
+ #endif
+ #include "libavutil/avstring.h"
+ /**
+  * @file
+  * URL utility functions.
+  */
+ int ff_url_join(char *str, int size, const char *proto,
+                 const char *authorization, const char *hostname,
+                 int port, const char *fmt, ...)
+ {
+ #if CONFIG_NETWORK
+     struct addrinfo hints = { 0 }, *ai;
+ #endif
+     str[0] = '\0';
+     if (proto)
+         av_strlcatf(str, size, "%s://", proto);
+     if (authorization && authorization[0])
+         av_strlcatf(str, size, "%s@", authorization);
+ #if CONFIG_NETWORK && defined(AF_INET6)
+     /* Determine if hostname is a numerical IPv6 address,
+      * properly escape it within [] in that case. */
+     hints.ai_flags = AI_NUMERICHOST;
+     if (!getaddrinfo(hostname, NULL, &hints, &ai)) {
+         if (ai->ai_family == AF_INET6) {
+             av_strlcat(str, "[", size);
+             av_strlcat(str, hostname, size);
+             av_strlcat(str, "]", size);
+         } else {
+             av_strlcat(str, hostname, size);
+         }
+         freeaddrinfo(ai);
+     } else
+ #endif
+         /* Not an IPv6 address, just output the plain string. */
+         av_strlcat(str, hostname, size);
+     if (port >= 0)
+         av_strlcatf(str, size, ":%d", port);
+     if (fmt) {
+         va_list vl;
+         int len = strlen(str);
+         va_start(vl, fmt);
+         vsnprintf(str + len, size > len ? size - len : 0, fmt, vl);
+         va_end(vl);
+     }
+     return strlen(str);
+ }
+ void ff_make_absolute_url(char *buf, int size, const char *base,
+                           const char *rel)
+ {
+     char *sep, *path_query;
+     /* Absolute path, relative to the current server */
+     if (base && strstr(base, "://") && rel[0] == '/') {
+         if (base != buf)
+             av_strlcpy(buf, base, size);
+         sep = strstr(buf, "://");
+         if (sep) {
+             /* Take scheme from base url */
+             if (rel[1] == '/') {
+                 sep[1] = '\0';
+             } else {
+                 /* Take scheme and host from base url */
+                 sep += 3;
+                 sep = strchr(sep, '/');
+                 if (sep)
+                     *sep = '\0';
+             }
+         }
+         av_strlcat(buf, rel, size);
+         return;
+     }
+     /* If rel actually is an absolute url, just copy it */
+     if (!base || strstr(rel, "://") || rel[0] == '/') {
+         av_strlcpy(buf, rel, size);
+         return;
+     }
+     if (base != buf)
+         av_strlcpy(buf, base, size);
+     /* Strip off any query string from base */
+     path_query = strchr(buf, '?');
+     if (path_query != NULL)
+         *path_query = '\0';
+     /* Is relative path just a new query part? */
+     if (rel[0] == '?') {
+         av_strlcat(buf, rel, size);
+         return;
+     }
+     /* Remove the file name from the base url */
+     sep = strrchr(buf, '/');
+     if (sep)
+         sep[1] = '\0';
+     else
+         buf[0] = '\0';
+     while (av_strstart(rel, "../", NULL) && sep) {
+         /* Remove the path delimiter at the end */
+         sep[0] = '\0';
+         sep = strrchr(buf, '/');
+         /* If the next directory name to pop off is "..", break here */
+         if (!strcmp(sep ? &sep[1] : buf, "..")) {
+             /* Readd the slash we just removed */
+             av_strlcat(buf, "/", size);
+             break;
+         }
+         /* Cut off the directory name */
+         if (sep)
+             sep[1] = '\0';
+         else
+             buf[0] = '\0';
+         rel += 3;
+     }
+     av_strlcat(buf, rel, size);
+ }
@@@ -248,4 -246,41 +248,41 @@@ URLProtocol *ffurl_protocol_next(URLPro
  int ff_udp_set_remote_url(URLContext *h, const char *uri);
  int ff_udp_get_local_port(URLContext *h);
  
 -/*
+ /**
+  * Assemble a URL string from components. This is the reverse operation
+  * of av_url_split.
+  *
+  * Note, this requires networking to be initialized, so the caller must
+  * ensure ff_network_init has been called.
+  *
+  * @see av_url_split
+  *
+  * @param str the buffer to fill with the url
+  * @param size the size of the str buffer
+  * @param proto the protocol identifier, if null, the separator
+  *              after the identifier is left out, too
+  * @param authorization an optional authorization string, may be null.
+  *                      An empty string is treated the same as a null string.
+  * @param hostname the host name string
+  * @param port the port number, left out from the string if negative
+  * @param fmt a generic format string for everything to add after the
+  *            host/port, may be null
+  * @return the number of characters written to the destination buffer
+  */
+ int ff_url_join(char *str, int size, const char *proto,
+                 const char *authorization, const char *hostname,
+                 int port, const char *fmt, ...) av_printf_format(7, 8);
++/**
+  * Convert a relative url into an absolute url, given a base url.
+  *
+  * @param buf the buffer where output absolute url is written
+  * @param size the size of buf
+  * @param base the base url, may be equal to buf.
+  * @param rel the new url, which is interpreted relative to base
+  */
+ void ff_make_absolute_url(char *buf, int size, const char *base,
+                           const char *rel);
  #endif /* AVFORMAT_URL_H */
@@@ -3948,81 -3173,13 +3904,12 @@@ int ff_find_stream_index(AVFormatContex
      return -1;
  }
  
- void ff_make_absolute_url(char *buf, int size, const char *base,
-                           const char *rel)
- {
-     char *sep, *path_query;
-     /* Absolute path, relative to the current server */
-     if (base && strstr(base, "://") && rel[0] == '/') {
-         if (base != buf)
-             av_strlcpy(buf, base, size);
-         sep = strstr(buf, "://");
-         if (sep) {
-             /* Take scheme from base url */
-             if (rel[1] == '/') {
-                 sep[1] = '\0';
-             } else {
-                 /* Take scheme and host from base url */
-                 sep += 3;
-                 sep = strchr(sep, '/');
-                 if (sep)
-                     *sep = '\0';
-             }
-         }
-         av_strlcat(buf, rel, size);
-         return;
-     }
-     /* If rel actually is an absolute url, just copy it */
-     if (!base || strstr(rel, "://") || rel[0] == '/') {
-         av_strlcpy(buf, rel, size);
-         return;
-     }
-     if (base != buf)
-         av_strlcpy(buf, base, size);
-     /* Strip off any query string from base */
-     path_query = strchr(buf, '?');
-     if (path_query != NULL)
-         *path_query = '\0';
-     /* Is relative path just a new query part? */
-     if (rel[0] == '?') {
-         av_strlcat(buf, rel, size);
-         return;
-     }
-     /* Remove the file name from the base url */
-     sep = strrchr(buf, '/');
-     if (sep)
-         sep[1] = '\0';
-     else
-         buf[0] = '\0';
-     while (av_strstart(rel, "../", NULL) && sep) {
-         /* Remove the path delimiter at the end */
-         sep[0] = '\0';
-         sep = strrchr(buf, '/');
-         /* If the next directory name to pop off is "..", break here */
-         if (!strcmp(sep ? &sep[1] : buf, "..")) {
-             /* Readd the slash we just removed */
-             av_strlcat(buf, "/", size);
-             break;
-         }
-         /* Cut off the directory name */
-         if (sep)
-             sep[1] = '\0';
-         else
-             buf[0] = '\0';
-         rel += 3;
-     }
-     av_strlcat(buf, rel, size);
- }
  int64_t ff_iso8601_to_unix_time(const char *datestr)
  {
 -#if HAVE_STRPTIME
      struct tm time1 = {0}, time2 = {0};
      char *ret1, *ret2;
 -    ret1 = strptime(datestr, "%Y - %m - %d %T", &time1);
 -    ret2 = strptime(datestr, "%Y - %m - %dT%T", &time2);
 +    ret1 = av_small_strptime(datestr, "%Y - %m - %d %H:%M:%S", &time1);
 +    ret2 = av_small_strptime(datestr, "%Y - %m - %dT%H:%M:%S", &time2);
      if (ret2 && !ret1)
          return av_timegm(&time2);
      else