Merge commit '71549a857b13edf4c4f95037de6ed5bb4c4bd4af'
authorMichael Niedermayer <michaelni@gmx.at>
Sun, 13 Oct 2013 22:37:01 +0000 (00:37 +0200)
committerMichael Niedermayer <michaelni@gmx.at>
Sun, 13 Oct 2013 22:37:01 +0000 (00:37 +0200)
* commit '71549a857b13edf4c4f95037de6ed5bb4c4bd4af':
  http: Support auth method detection for POST

Conflicts:
libavformat/version.h

Merged-by: Michael Niedermayer <michaelni@gmx.at>
1  2 
libavformat/http.c
libavformat/version.h

diff --combined libavformat/http.c
@@@ -1,21 -1,21 +1,21 @@@
  /*
 - * HTTP protocol for avconv client
 + * HTTP protocol for ffmpeg client
   * Copyright (c) 2000, 2001 Fabrice Bellard
   *
 - * This file is part of Libav.
 + * This file is part of FFmpeg.
   *
 - * Libav is free software; you can redistribute it and/or
 + * 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.
   *
 - * Libav is distributed in the hope that it will be useful,
 + * 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 Libav; if not, write to the Free Software
 + * License along with FFmpeg; if not, write to the Free Software
   * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
   */
  
@@@ -33,7 -33,7 +33,7 @@@
  #include <zlib.h>
  #endif
  
 -/* XXX: POST protocol is not completely implemented because avconv uses
 +/* XXX: POST protocol is not completely implemented because ffmpeg uses
     only a subset of it. */
  
  /* The IO buffer size is unrelated to the max URL size in itself, but needs
@@@ -50,57 -50,39 +50,59 @@@ typedef struct 
      int line_count;
      int http_code;
      int64_t chunksize;      /**< Used if "Transfer-Encoding: chunked" otherwise -1. */
 +    char *content_type;
 +    char *user_agent;
      int64_t off, filesize;
 +    int icy_data_read;      ///< how much data was read since last ICY metadata packet
 +    int icy_metaint;        ///< after how many bytes of read data a new metadata packet will be found
      char location[MAX_URL_SIZE];
      HTTPAuthState auth_state;
      HTTPAuthState proxy_auth_state;
      char *headers;
      int willclose;          /**< Set if the server correctly handles Connection: close and will close the connection after feeding us the content. */
 +    int seekable;           /**< Control seekability, 0 = disable, 1 = enable, -1 = probe. */
      int chunked_post;
      int end_chunked_post;   /**< A flag which indicates if the end of chunked encoding has been sent. */
      int end_header;         /**< A flag which indicates we have finished to read POST reply. */
      int multiple_requests;  /**< A flag which indicates if we use persistent connections. */
      uint8_t *post_data;
      int post_datalen;
 +    int is_akamai;
 +    char *mime_type;
 +    char *cookies;          ///< holds newline (\n) delimited Set-Cookie header field values (without the "Set-Cookie: " field name)
 +    int icy;
 +    char *icy_metadata_headers;
 +    char *icy_metadata_packet;
  #if CONFIG_ZLIB
      int compressed;
      z_stream inflate_stream;
      uint8_t *inflate_buffer;
  #endif
      AVDictionary *chained_options;
+     int send_expect_100;
  } HTTPContext;
  
  #define OFFSET(x) offsetof(HTTPContext, x)
  #define D AV_OPT_FLAG_DECODING_PARAM
  #define E AV_OPT_FLAG_ENCODING_PARAM
 +#define DEFAULT_USER_AGENT "Lavf/" AV_STRINGIFY(LIBAVFORMAT_VERSION)
  static const AVOption options[] = {
 +{"seekable", "control seekability of connection", OFFSET(seekable), AV_OPT_TYPE_INT, {.i64 = -1}, -1, 1, D },
  {"chunked_post", "use chunked transfer-encoding for posts", OFFSET(chunked_post), AV_OPT_TYPE_INT, {.i64 = 1}, 0, 1, E },
 -{"headers", "custom HTTP headers, can override built in default headers", OFFSET(headers), AV_OPT_TYPE_STRING, { 0 }, 0, 0, D|E },
 +{"headers", "set custom HTTP headers, can override built in default headers", OFFSET(headers), AV_OPT_TYPE_STRING, { 0 }, 0, 0, D|E },
 +{"content_type", "force a content type", OFFSET(content_type), AV_OPT_TYPE_STRING, { 0 }, 0, 0, D|E },
 +{"user-agent", "override User-Agent header", OFFSET(user_agent), AV_OPT_TYPE_STRING, {.str = DEFAULT_USER_AGENT}, 0, 0, D },
  {"multiple_requests", "use persistent connections", OFFSET(multiple_requests), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, D|E },
 -{"post_data", "custom HTTP post data", OFFSET(post_data), AV_OPT_TYPE_BINARY, .flags = D|E },
 +{"post_data", "set custom HTTP post data", OFFSET(post_data), AV_OPT_TYPE_BINARY, .flags = D|E },
 +{"mime_type", "set MIME type", OFFSET(mime_type), AV_OPT_TYPE_STRING, {0}, 0, 0, 0 },
 +{"cookies", "set cookies to be sent in applicable future requests, use newline delimited Set-Cookie HTTP field value syntax", OFFSET(cookies), AV_OPT_TYPE_STRING, {0}, 0, 0, 0 },
 +{"icy", "request ICY metadata", OFFSET(icy), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, D },
 +{"icy_metadata_headers", "return ICY metadata headers", OFFSET(icy_metadata_headers), AV_OPT_TYPE_STRING, {0}, 0, 0, 0 },
 +{"icy_metadata_packet", "return current ICY metadata packet", OFFSET(icy_metadata_packet), AV_OPT_TYPE_STRING, {0}, 0, 0, 0 },
  {"auth_type", "HTTP authentication type", OFFSET(auth_state.auth_type), AV_OPT_TYPE_INT, {.i64 = HTTP_AUTH_NONE}, HTTP_AUTH_NONE, HTTP_AUTH_BASIC, D|E, "auth_type" },
  {"none", "No auth method set, autodetect", 0, AV_OPT_TYPE_CONST, {.i64 = HTTP_AUTH_NONE}, 0, 0, D|E, "auth_type" },
  {"basic", "HTTP basic authentication", 0, AV_OPT_TYPE_CONST, {.i64 = HTTP_AUTH_BASIC}, 0, 0, D|E, "auth_type" },
+ {"send_expect_100", "Force sending an Expect: 100-continue header for POST", OFFSET(send_expect_100), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, E, "auth_type" },
  {NULL}
  };
  #define HTTP_CLASS(flavor)\
@@@ -192,7 -174,8 +194,7 @@@ static int http_open_cnx(URLContext *h
      if (s->http_code == 401) {
          if ((cur_auth_type == HTTP_AUTH_NONE || s->auth_state.stale) &&
              s->auth_state.auth_type != HTTP_AUTH_NONE && attempts < 4) {
 -            ffurl_close(s->hd);
 -            s->hd = NULL;
 +            ffurl_closep(&s->hd);
              goto redo;
          } else
              goto fail;
      if (s->http_code == 407) {
          if ((cur_proxy_auth_type == HTTP_AUTH_NONE || s->proxy_auth_state.stale) &&
              s->proxy_auth_state.auth_type != HTTP_AUTH_NONE && attempts < 4) {
 -            ffurl_close(s->hd);
 -            s->hd = NULL;
 +            ffurl_closep(&s->hd);
              goto redo;
          } else
              goto fail;
      if ((s->http_code == 301 || s->http_code == 302 || s->http_code == 303 || s->http_code == 307)
          && location_changed == 1) {
          /* url moved, get next */
 -        ffurl_close(s->hd);
 -        s->hd = NULL;
 +        ffurl_closep(&s->hd);
          if (redirects++ >= MAX_REDIRECTS)
              return AVERROR(EIO);
          /* Restart the authentication process with the new target, which
      return 0;
   fail:
      if (s->hd)
 -        ffurl_close(s->hd);
 -    s->hd = NULL;
 +        ffurl_closep(&s->hd);
      return AVERROR(EIO);
  }
  
@@@ -232,7 -218,6 +234,7 @@@ int ff_http_do_new_request(URLContext *
      int ret;
  
      s->off = 0;
 +    s->icy_data_read = 0;
      av_strlcpy(s->location, uri, sizeof(s->location));
  
      av_dict_copy(&options, s->chained_options, 0);
@@@ -247,10 -232,7 +249,10 @@@ static int http_open(URLContext *h, con
      HTTPContext *s = h->priv_data;
      int ret;
  
 -    h->is_streamed = 1;
 +    if( s->seekable == 1 )
 +        h->is_streamed = 0;
 +    else
 +        h->is_streamed = 1;
  
      s->filesize = -1;
      av_strlcpy(s->location, uri, sizeof(s->location));
@@@ -314,7 -296,6 +316,7 @@@ static int process_line(URLContext *h, 
  {
      HTTPContext *s = h->priv_data;
      char *tag, *p, *end;
 +    char redirected_location[MAX_URL_SIZE];
  
      /* end of header */
      if (line[0] == '\0') {
          while (av_isspace(*p))
              p++;
          if (!av_strcasecmp(tag, "Location")) {
 -            av_strlcpy(s->location, p, sizeof(s->location));
 +            ff_make_absolute_url(redirected_location, sizeof(redirected_location), s->location, p);
 +            av_strlcpy(s->location, redirected_location, sizeof(s->location));
              *new_location = 1;
          } else if (!av_strcasecmp (tag, "Content-Length") && s->filesize == -1) {
              s->filesize = strtoll(p, NULL, 10);
                  if ((slash = strchr(p, '/')) && strlen(slash) > 0)
                      s->filesize = strtoll(slash+1, NULL, 10);
              }
 -            h->is_streamed = 0; /* we _can_ in fact seek */
 -        } else if (!av_strcasecmp(tag, "Accept-Ranges") && !strncmp(p, "bytes", 5)) {
 +            if (s->seekable == -1 && (!s->is_akamai || s->filesize != 2147483647))
 +                h->is_streamed = 0; /* we _can_ in fact seek */
 +        } else if (!av_strcasecmp(tag, "Accept-Ranges") && !strncmp(p, "bytes", 5) && s->seekable == -1) {
              h->is_streamed = 0;
          } else if (!av_strcasecmp (tag, "Transfer-Encoding") && !av_strncasecmp(p, "chunked", 7)) {
              s->filesize = -1;
          } else if (!av_strcasecmp (tag, "Connection")) {
              if (!strcmp(p, "close"))
                  s->willclose = 1;
 +        } else if (!av_strcasecmp (tag, "Server") && !av_strcasecmp (p, "AkamaiGHost")) {
 +            s->is_akamai = 1;
 +        } else if (!av_strcasecmp (tag, "Content-Type")) {
 +            av_free(s->mime_type); s->mime_type = av_strdup(p);
 +        } else if (!av_strcasecmp (tag, "Set-Cookie")) {
 +            if (!s->cookies) {
 +                if (!(s->cookies = av_strdup(p)))
 +                    return AVERROR(ENOMEM);
 +            } else {
 +                char *tmp = s->cookies;
 +                size_t str_size = strlen(tmp) + strlen(p) + 2;
 +                if (!(s->cookies = av_malloc(str_size))) {
 +                    s->cookies = tmp;
 +                    return AVERROR(ENOMEM);
 +                }
 +                snprintf(s->cookies, str_size, "%s\n%s", tmp, p);
 +                av_free(tmp);
 +            }
 +        } else if (!av_strcasecmp (tag, "Icy-MetaInt")) {
 +            s->icy_metaint = strtoll(p, NULL, 10);
 +        } else if (!av_strncasecmp(tag, "Icy-", 4)) {
 +            // Concat all Icy- header lines
 +            char *buf = av_asprintf("%s%s: %s\n",
 +                s->icy_metadata_headers ? s->icy_metadata_headers : "", tag, p);
 +            if (!buf)
 +                return AVERROR(ENOMEM);
 +            av_freep(&s->icy_metadata_headers);
 +            s->icy_metadata_headers = buf;
          } else if (!av_strcasecmp (tag, "Content-Encoding")) {
              if (!av_strncasecmp(p, "gzip", 4) || !av_strncasecmp(p, "deflate", 7)) {
  #if CONFIG_ZLIB
      return 1;
  }
  
 +/**
 + * Create a string containing cookie values for use as a HTTP cookie header
 + * field value for a particular path and domain from the cookie values stored in
 + * the HTTP protocol context. The cookie string is stored in *cookies.
 + *
 + * @return a negative value if an error condition occurred, 0 otherwise
 + */
 +static int get_cookies(HTTPContext *s, char **cookies, const char *path,
 +                       const char *domain)
 +{
 +    // cookie strings will look like Set-Cookie header field values.  Multiple
 +    // Set-Cookie fields will result in multiple values delimited by a newline
 +    int ret = 0;
 +    char *next, *cookie, *set_cookies = av_strdup(s->cookies), *cset_cookies = set_cookies;
 +
 +    if (!set_cookies) return AVERROR(EINVAL);
 +
 +    *cookies = NULL;
 +    while ((cookie = av_strtok(set_cookies, "\n", &next))) {
 +        int domain_offset = 0;
 +        char *param, *next_param, *cdomain = NULL, *cpath = NULL, *cvalue = NULL;
 +        set_cookies = NULL;
 +
 +        while ((param = av_strtok(cookie, "; ", &next_param))) {
 +            cookie = NULL;
 +            if        (!av_strncasecmp("path=",   param, 5)) {
 +                av_free(cpath);
 +                cpath = av_strdup(&param[5]);
 +            } else if (!av_strncasecmp("domain=", param, 7)) {
 +                av_free(cdomain);
 +                cdomain = av_strdup(&param[7]);
 +            } else if (!av_strncasecmp("secure",  param, 6) ||
 +                       !av_strncasecmp("comment", param, 7) ||
 +                       !av_strncasecmp("max-age", param, 7) ||
 +                       !av_strncasecmp("version", param, 7)) {
 +                // ignore Comment, Max-Age, Secure and Version
 +            } else {
 +                av_free(cvalue);
 +                cvalue = av_strdup(param);
 +            }
 +        }
 +        if (!cdomain)
 +            cdomain = av_strdup(domain);
 +
 +        // ensure all of the necessary values are valid
 +        if (!cdomain || !cpath || !cvalue) {
 +            av_log(s, AV_LOG_WARNING,
 +                   "Invalid cookie found, no value, path or domain specified\n");
 +            goto done_cookie;
 +        }
 +
 +        // check if the request path matches the cookie path
 +        if (av_strncasecmp(path, cpath, strlen(cpath)))
 +            goto done_cookie;
 +
 +        // the domain should be at least the size of our cookie domain
 +        domain_offset = strlen(domain) - strlen(cdomain);
 +        if (domain_offset < 0)
 +            goto done_cookie;
 +
 +        // match the cookie domain
 +        if (av_strcasecmp(&domain[domain_offset], cdomain))
 +            goto done_cookie;
 +
 +        // cookie parameters match, so copy the value
 +        if (!*cookies) {
 +            if (!(*cookies = av_strdup(cvalue))) {
 +                ret = AVERROR(ENOMEM);
 +                goto done_cookie;
 +            }
 +        } else {
 +            char *tmp = *cookies;
 +            size_t str_size = strlen(cvalue) + strlen(*cookies) + 3;
 +            if (!(*cookies = av_malloc(str_size))) {
 +                ret = AVERROR(ENOMEM);
 +                goto done_cookie;
 +            }
 +            snprintf(*cookies, str_size, "%s; %s", tmp, cvalue);
 +            av_free(tmp);
 +        }
 +
 +        done_cookie:
 +        av_free(cdomain);
 +        av_free(cpath);
 +        av_free(cvalue);
 +        if (ret < 0) {
 +            if (*cookies) av_freep(cookies);
 +            av_free(cset_cookies);
 +            return ret;
 +        }
 +    }
 +
 +    av_free(cset_cookies);
 +
 +    return 0;
 +}
 +
  static inline int has_header(const char *str, const char *header)
  {
      /* header + 2 to skip over CRLF prefix. (make sure you have one!) */
@@@ -578,11 -432,12 +580,12 @@@ static int http_connect(URLContext *h, 
  {
      HTTPContext *s = h->priv_data;
      int post, err;
 -    char headers[1024] = "";
 +    char headers[4096] = "";
      char *authstr = NULL, *proxyauthstr = NULL;
      int64_t off = s->off;
      int len = 0;
      const char *method;
+     int send_expect_100 = 0;
  
  
      /* send http header */
                                             method);
      proxyauthstr = ff_http_auth_create_response(&s->proxy_auth_state, proxyauth,
                                                  local_path, method);
+     if (post && !s->post_data) {
+         send_expect_100 = s->send_expect_100;
+         /* The user has supplied authentication but we don't know the auth type,
+          * send Expect: 100-continue to get the 401 response including the
+          * WWW-Authenticate header, or an 100 continue if no auth actually
+          * is needed. */
+         if (auth && s->auth_state.auth_type == HTTP_AUTH_NONE &&
+             s->http_code != 401)
+             send_expect_100 = 1;
+     }
  
      /* set default headers if needed */
      if (!has_header(s->headers, "\r\nUser-Agent: "))
 -       len += av_strlcatf(headers + len, sizeof(headers) - len,
 -                          "User-Agent: %s\r\n", LIBAVFORMAT_IDENT);
 +        len += av_strlcatf(headers + len, sizeof(headers) - len,
 +                           "User-Agent: %s\r\n", s->user_agent);
      if (!has_header(s->headers, "\r\nAccept: "))
          len += av_strlcpy(headers + len, "Accept: */*\r\n",
                            sizeof(headers) - len);
 -    if (!has_header(s->headers, "\r\nRange: ") && !post)
 +    // Note: we send this on purpose even when s->off is 0 when we're probing,
 +    // since it allows us to detect more reliably if a (non-conforming)
 +    // server supports seeking by analysing the reply headers.
 +    if (!has_header(s->headers, "\r\nRange: ") && !post && (s->off > 0 || s->seekable == -1))
          len += av_strlcatf(headers + len, sizeof(headers) - len,
                             "Range: bytes=%"PRId64"-\r\n", s->off);
+     if (send_expect_100 && !has_header(s->headers, "\r\nExpect: "))
+         len += av_strlcatf(headers + len, sizeof(headers) - len,
+                            "Expect: 100-continue\r\n");
  
      if (!has_header(s->headers, "\r\nConnection: ")) {
          if (s->multiple_requests) {
      if (!has_header(s->headers, "\r\nContent-Length: ") && s->post_data)
          len += av_strlcatf(headers + len, sizeof(headers) - len,
                             "Content-Length: %d\r\n", s->post_datalen);
 +    if (!has_header(s->headers, "\r\nContent-Type: ") && s->content_type)
 +        len += av_strlcatf(headers + len, sizeof(headers) - len,
 +                           "Content-Type: %s\r\n", s->content_type);
 +    if (!has_header(s->headers, "\r\nCookie: ") && s->cookies) {
 +        char *cookies = NULL;
 +        if (!get_cookies(s, &cookies, path, hoststr)) {
 +            len += av_strlcatf(headers + len, sizeof(headers) - len,
 +                               "Cookie: %s\r\n", cookies);
 +            av_free(cookies);
 +        }
 +    }
 +    if (!has_header(s->headers, "\r\nIcy-MetaData: ") && s->icy) {
 +        len += av_strlcatf(headers + len, sizeof(headers) - len,
 +                           "Icy-MetaData: %d\r\n", 1);
 +    }
  
      /* now add in custom headers */
      if (s->headers)
      s->buf_end = s->buffer;
      s->line_count = 0;
      s->off = 0;
 +    s->icy_data_read = 0;
      s->filesize = -1;
      s->willclose = 0;
      s->end_chunked_post = 0;
      s->end_header = 0;
-     if (post && !s->post_data) {
+     if (post && !s->post_data && !send_expect_100) {
          /* Pretend that it did work. We didn't read any header yet, since
           * we've still to send the POST data, but the code calling this
           * function will check http_code after we return. */
@@@ -719,7 -568,6 +735,7 @@@ static int http_buf_read(URLContext *h
      }
      if (len > 0) {
          s->off += len;
 +        s->icy_data_read += len;
          if (s->chunksize > 0)
              s->chunksize -= len;
      }
@@@ -793,32 -641,6 +809,32 @@@ static int http_read(URLContext *h, uin
          }
          size = FFMIN(size, s->chunksize);
      }
 +    if (s->icy_metaint > 0) {
 +        int remaining = s->icy_metaint - s->icy_data_read; /* until next metadata packet */
 +        if (!remaining) {
 +            // The metadata packet is variable sized. It has a 1 byte header
 +            // which sets the length of the packet (divided by 16). If it's 0,
 +            // the metadata doesn't change. After the packet, icy_metaint bytes
 +            // of normal data follow.
 +            int ch = http_getc(s);
 +            if (ch < 0)
 +                return ch;
 +            if (ch > 0) {
 +                char data[255 * 16 + 1];
 +                int n;
 +                int ret;
 +                ch *= 16;
 +                for (n = 0; n < ch; n++)
 +                    data[n] = http_getc(s);
 +                data[ch + 1] = 0;
 +                if ((ret = av_opt_set(s, "icy_metadata_packet", data, 0)) < 0)
 +                    return ret;
 +            }
 +            s->icy_data_read = 0;
 +            remaining = s->icy_metaint;
 +        }
 +        size = FFMIN(size, remaining);
 +    }
  #if CONFIG_ZLIB
      if (s->compressed)
          return http_buf_read_compressed(h, buf, size);
@@@ -885,7 -707,7 +901,7 @@@ static int http_close(URLContext *h
      }
  
      if (s->hd)
 -        ffurl_close(s->hd);
 +        ffurl_closep(&s->hd);
      av_dict_free(&s->chained_options);
      return ret;
  }
@@@ -973,7 -795,7 +989,7 @@@ static int http_proxy_close(URLContext 
  {
      HTTPContext *s = h->priv_data;
      if (s->hd)
 -        ffurl_close(s->hd);
 +        ffurl_closep(&s->hd);
      return 0;
  }
  
@@@ -988,10 -810,7 +1004,10 @@@ static int http_proxy_open(URLContext *
      char *authstr;
      int new_loc;
  
 -    h->is_streamed = 1;
 +    if( s->seekable == 1 )
 +        h->is_streamed = 0;
 +    else
 +        h->is_streamed = 1;
  
      av_url_split(NULL, 0, auth, sizeof(auth), hostname, sizeof(hostname), &port,
                   pathbuf, sizeof(pathbuf), uri);
@@@ -1047,7 -866,8 +1063,7 @@@ redo
      if (s->http_code == 407 &&
          (cur_auth_type == HTTP_AUTH_NONE || s->proxy_auth_state.stale) &&
          s->proxy_auth_state.auth_type != HTTP_AUTH_NONE && attempts < 2) {
 -        ffurl_close(s->hd);
 -        s->hd = NULL;
 +        ffurl_closep(&s->hd);
          goto redo;
      }
  
diff --combined libavformat/version.h
@@@ -1,20 -1,20 +1,20 @@@
  /*
   * Version macros.
   *
 - * This file is part of Libav.
 + * This file is part of FFmpeg.
   *
 - * Libav is free software; you can redistribute it and/or
 + * 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.
   *
 - * Libav is distributed in the hope that it will be useful,
 + * 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 Libav; if not, write to the Free Software
 + * License along with FFmpeg; if not, write to the Free Software
   * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
   */
  
@@@ -30,8 -30,8 +30,8 @@@
  #include "libavutil/avutil.h"
  
  #define LIBAVFORMAT_VERSION_MAJOR 55
 -#define LIBAVFORMAT_VERSION_MINOR  6
 -#define LIBAVFORMAT_VERSION_MICRO  2
 +#define LIBAVFORMAT_VERSION_MINOR 19
- #define LIBAVFORMAT_VERSION_MICRO 101
++#define LIBAVFORMAT_VERSION_MICRO 102
  
  #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \
                                                 LIBAVFORMAT_VERSION_MINOR, \
   * the public API and may change, break or disappear at any time.
   */
  
 +#ifndef FF_API_ALLOC_OUTPUT_CONTEXT
 +#define FF_API_ALLOC_OUTPUT_CONTEXT    (LIBAVFORMAT_VERSION_MAJOR < 56)
 +#endif
 +#ifndef FF_API_FORMAT_PARAMETERS
 +#define FF_API_FORMAT_PARAMETERS       (LIBAVFORMAT_VERSION_MAJOR < 56)
 +#endif
 +#ifndef FF_API_NEW_STREAM
 +#define FF_API_NEW_STREAM              (LIBAVFORMAT_VERSION_MAJOR < 56)
 +#endif
 +#ifndef FF_API_SET_PTS_INFO
 +#define FF_API_SET_PTS_INFO            (LIBAVFORMAT_VERSION_MAJOR < 56)
 +#endif
 +#ifndef FF_API_CLOSE_INPUT_FILE
 +#define FF_API_CLOSE_INPUT_FILE        (LIBAVFORMAT_VERSION_MAJOR < 56)
 +#endif
 +#ifndef FF_API_READ_PACKET
 +#define FF_API_READ_PACKET             (LIBAVFORMAT_VERSION_MAJOR < 56)
 +#endif
 +#ifndef FF_API_ASS_SSA
 +#define FF_API_ASS_SSA                 (LIBAVFORMAT_VERSION_MAJOR < 56)
 +#endif
 +#ifndef FF_API_R_FRAME_RATE
 +#define FF_API_R_FRAME_RATE            1
 +#endif
  #endif /* AVFORMAT_VERSION_H */