Merge commit '9f61abc8111c7c43f49ca012e957a108b9cc7610'
authorDerek Buitenhuis <derek.buitenhuis@gmail.com>
Wed, 10 Feb 2016 14:40:32 +0000 (14:40 +0000)
committerDerek Buitenhuis <derek.buitenhuis@gmail.com>
Wed, 10 Feb 2016 14:42:41 +0000 (14:42 +0000)
This also deprecates our old duplicated callbacks.

* commit '9f61abc8111c7c43f49ca012e957a108b9cc7610':
  lavf: allow custom IO for all files

Merged-by: Derek Buitenhuis <derek.buitenhuis@gmail.com>
19 files changed:
1  2 
doc/APIchanges
libavformat/avformat.h
libavformat/dashenc.c
libavformat/hdsenc.c
libavformat/hls.c
libavformat/hlsenc.c
libavformat/img2dec.c
libavformat/img2enc.c
libavformat/internal.h
libavformat/mlvdec.c
libavformat/mov.c
libavformat/movenc.c
libavformat/options.c
libavformat/segment.c
libavformat/smoothstreamingenc.c
libavformat/tee.c
libavformat/utils.c
libavformat/version.h
libavformat/webm_chunk.c

diff --cc doc/APIchanges
@@@ -15,24 -13,14 +15,27 @@@ libavutil:     2015-08-2
  
  API changes, most recent first:
  
 -2016-xx-xx - xxxxxxx - lavf 57.3.0 - avformat.h
++2016-xx-xx - xxxxxxx - lavf 57.25.0 - avformat.h
+   Add AVFormatContext.opaque, io_open and io_close, allowing custom IO
 -  for muxers and demuxers that open additional files.
 -2015-xx-xx - xxxxxxx - lavc 57.12.0 - avcodec.h
 +2016-02-01 - xxxxxxx - lavf 57.24.100
 +  Add protocol_whitelist to AVFormatContext, AVIOContext
 +
 +2016-01-31 - xxxxxxx - lavu 55.17.100
 +  Add AV_FRAME_DATA_GOP_TIMECODE for exporting MPEG1/2 GOP timecodes.
 +
 +2016-01-01 - xxxxxxx - lavc 57.21.100 / 57.12.0 - avcodec.h
    Add AVCodecDescriptor.profiles and avcodec_profile_name().
  
 -2015-xx-xx - xxxxxxx - lavc 57.11.0 - avcodec.h dirac.h
 +2015-12-28 - xxxxxxx - lavf 57.21.100 - avformat.h
 +  Add automatic bitstream filtering; add av_apply_bitstream_filters()
 +
 +2015-12-22 - xxxxxxx - lavfi 6.21.101 - avfilter.h
 +  Deprecate avfilter_link_set_closed().
 +  Applications are not supposed to mess with links,
 +  they should close the sinks.
 +
 +2015-12-17 - lavc 57.18.100 / 57.11.0 - avcodec.h dirac.h
    xxxxxxx - Add av_packet_add_side_data().
    xxxxxxx - Add AVCodecContext.coded_side_data.
    xxxxxxx - Add AVCPBProperties API.
@@@ -1731,142 -1231,39 +1731,176 @@@ typedef struct AVFormatContext 
      AVFormatInternal *internal;
  
      /**
 -     * Arbitrary user data set by the caller.
 +     * IO repositioned flag.
 +     * This is set by avformat when the underlaying IO context read pointer
 +     * is repositioned, for example when doing byte based seeking.
 +     * Demuxers can use the flag to detect such changes.
 +     */
 +    int io_repositioned;
 +
 +    /**
 +     * Forced video codec.
 +     * This allows forcing a specific decoder, even when there are multiple with
 +     * the same codec_id.
 +     * Demuxing: Set by user via av_format_set_video_codec (NO direct access).
 +     */
 +    AVCodec *video_codec;
 +
 +    /**
 +     * Forced audio codec.
 +     * This allows forcing a specific decoder, even when there are multiple with
 +     * the same codec_id.
 +     * Demuxing: Set by user via av_format_set_audio_codec (NO direct access).
 +     */
 +    AVCodec *audio_codec;
 +
 +    /**
 +     * Forced subtitle codec.
 +     * This allows forcing a specific decoder, even when there are multiple with
 +     * the same codec_id.
 +     * Demuxing: Set by user via av_format_set_subtitle_codec (NO direct access).
 +     */
 +    AVCodec *subtitle_codec;
 +
 +    /**
 +     * Forced data codec.
 +     * This allows forcing a specific decoder, even when there are multiple with
 +     * the same codec_id.
 +     * Demuxing: Set by user via av_format_set_data_codec (NO direct access).
 +     */
 +    AVCodec *data_codec;
 +
 +    /**
 +     * Number of bytes to be written as padding in a metadata header.
 +     * Demuxing: Unused.
 +     * Muxing: Set by user via av_format_set_metadata_header_padding.
 +     */
 +    int metadata_header_padding;
 +
 +    /**
 +     * User data.
 +     * This is a place for some private data of the user.
-      * Mostly usable with control_message_cb or any future callbacks in device's context.
       */
      void *opaque;
  
      /**
 +     * Callback used by devices to communicate with application.
 +     */
 +    av_format_control_message control_message_cb;
 +
 +    /**
 +     * Output timestamp offset, in microseconds.
 +     * Muxing: set by user via AVOptions (NO direct access)
 +     */
 +    int64_t output_ts_offset;
 +
 +    /**
 +     * dump format separator.
 +     * can be ", " or "\n      " or anything else
 +     * Code outside libavformat should access this field using AVOptions
 +     * (NO direct access).
 +     * - muxing: Set by user.
 +     * - demuxing: Set by user.
 +     */
 +    uint8_t *dump_separator;
 +
 +    /**
 +     * Forced Data codec_id.
 +     * Demuxing: Set by user.
 +     */
 +    enum AVCodecID data_codec_id;
 +
++#if FF_API_OLD_OPEN_CALLBACKS
 +    /**
 +     * Called to open further IO contexts when needed for demuxing.
 +     *
 +     * This can be set by the user application to perform security checks on
 +     * the URLs before opening them.
 +     * The function should behave like avio_open2(), AVFormatContext is provided
 +     * as contextual information and to reach AVFormatContext.opaque.
 +     *
 +     * If NULL then some simple checks are used together with avio_open2().
 +     *
 +     * Must not be accessed directly from outside avformat.
 +     * @See av_format_set_open_cb()
 +     *
 +     * Demuxing: Set by user.
++     *
++     * @deprecated Use io_open and io_close.
 +     */
++    attribute_deprecated
 +    int (*open_cb)(struct AVFormatContext *s, AVIOContext **p, const char *url, int flags, const AVIOInterruptCB *int_cb, AVDictionary **options);
++#endif
 +
 +    /**
 +     * ',' separated list of allowed protocols.
 +     * - encoding: unused
 +     * - decoding: set by user through AVOptions (NO direct access)
 +     */
 +    char *protocol_whitelist;
++
++    /*
+      * A callback for opening new IO streams.
+      *
+      * Certain muxers or demuxers (e.g. for various playlist-based formats) need
+      * to open additional files during muxing or demuxing. This callback allows
+      * the caller to provide custom IO in such cases.
+      *
+      * @param s the format context
+      * @param pb on success, the newly opened IO context should be returned here
+      * @param url the url to open
+      * @param flags a combination of AVIO_FLAG_*
+      * @param options a dictionary of additional options, with the same
+      *                semantics as in avio_open2()
+      * @return 0 on success, a negative AVERROR code on failure
+      *
+      * @note Certain muxers and demuxers do nesting, i.e. they open one or more
+      * additional internal format contexts. Thus the AVFormatContext pointer
+      * passed to this callback may be different from the one facing the caller.
+      * It will, however, have the same 'opaque' field.
+      */
+     int (*io_open)(struct AVFormatContext *s, AVIOContext **pb, const char *url,
+                    int flags, AVDictionary **options);
+     /**
+      * A callback for closing the streams opened with AVFormatContext.io_open().
+      */
+     void (*io_close)(struct AVFormatContext *s, AVIOContext *pb);
  } AVFormatContext;
  
- AVOpenCallback av_format_get_open_cb(const AVFormatContext *s);
- void      av_format_set_open_cb(AVFormatContext *s, AVOpenCallback callback);
 +int av_format_get_probe_score(const AVFormatContext *s);
 +AVCodec * av_format_get_video_codec(const AVFormatContext *s);
 +void      av_format_set_video_codec(AVFormatContext *s, AVCodec *c);
 +AVCodec * av_format_get_audio_codec(const AVFormatContext *s);
 +void      av_format_set_audio_codec(AVFormatContext *s, AVCodec *c);
 +AVCodec * av_format_get_subtitle_codec(const AVFormatContext *s);
 +void      av_format_set_subtitle_codec(AVFormatContext *s, AVCodec *c);
 +AVCodec * av_format_get_data_codec(const AVFormatContext *s);
 +void      av_format_set_data_codec(AVFormatContext *s, AVCodec *c);
 +int       av_format_get_metadata_header_padding(const AVFormatContext *s);
 +void      av_format_set_metadata_header_padding(AVFormatContext *s, int c);
 +void *    av_format_get_opaque(const AVFormatContext *s);
 +void      av_format_set_opaque(AVFormatContext *s, void *opaque);
 +av_format_control_message av_format_get_control_message_cb(const AVFormatContext *s);
 +void      av_format_set_control_message_cb(AVFormatContext *s, av_format_control_message callback);
++#if FF_API_OLD_OPEN_CALLBACKS
++attribute_deprecated AVOpenCallback av_format_get_open_cb(const AVFormatContext *s);
++attribute_deprecated void av_format_set_open_cb(AVFormatContext *s, AVOpenCallback callback);
++#endif
 +
 +/**
 + * This function will cause global side data to be injected in the next packet
 + * of each stream as well as after any subsequent seek.
 + */
 +void av_format_inject_global_side_data(AVFormatContext *s);
 +
 +/**
 + * Returns the method used to set ctx->duration.
 + *
 + * @return AVFMT_DURATION_FROM_PTS, AVFMT_DURATION_FROM_STREAM, or AVFMT_DURATION_FROM_BITRATE.
 + */
 +enum AVDurationEstimationMethod av_fmt_ctx_get_duration_estimation_method(const AVFormatContext* ctx);
 +
  typedef struct AVPacketList {
      AVPacket pkt;
      struct AVPacketList *next;
@@@ -549,8 -532,8 +548,8 @@@ static int write_manifest(AVFormatConte
      avio_printf(out, "\t</Period>\n");
      avio_printf(out, "</MPD>\n");
      avio_flush(out);
-     avio_close(out);
+     ff_format_io_close(s, &out);
 -    return ff_rename(temp_filename, s->filename);
 +    return ff_rename(temp_filename, s->filename, s);
  }
  
  static int dash_write_header(AVFormatContext *s)
@@@ -140,19 -139,20 +140,20 @@@ static void hds_free(AVFormatContext *s
          return;
      for (i = 0; i < s->nb_streams; i++) {
          OutputStream *os = &c->streams[i];
-         avio_closep(&os->out);
+         if (os->out)
+             ff_format_io_close(s, &os->out);
          if (os->ctx && os->ctx_inited)
              av_write_trailer(os->ctx);
 -        if (os->ctx && os->ctx->pb)
 -            av_free(os->ctx->pb);
 +        if (os->ctx)
 +            av_freep(&os->ctx->pb);
          if (os->ctx)
              avformat_free_context(os->ctx);
 -        av_free(os->metadata);
 +        av_freep(&os->metadata);
          for (j = 0; j < os->nb_extra_packets; j++)
 -            av_free(os->extra_packets[j]);
 +            av_freep(&os->extra_packets[j]);
          for (j = 0; j < os->nb_fragments; j++)
 -            av_free(os->fragments[j]);
 -        av_free(os->fragments);
 +            av_freep(&os->fragments[j]);
 +        av_freep(&os->fragments);
      }
      av_freep(&c->streams);
  }
@@@ -202,8 -201,8 +202,8 @@@ static int write_manifest(AVFormatConte
      }
      avio_printf(out, "</manifest>\n");
      avio_flush(out);
-     avio_close(out);
+     ff_format_io_close(s, &out);
 -    return ff_rename(temp_filename, filename);
 +    return ff_rename(temp_filename, filename, s);
  }
  
  static void update_size(AVIOContext *out, int64_t pos)
@@@ -283,8 -281,8 +282,8 @@@ static int write_abst(AVFormatContext *
      }
      update_size(out, afrt_pos);
      update_size(out, 0);
-     avio_close(out);
+     ff_format_io_close(s, &out);
 -    return ff_rename(temp_filename, filename);
 +    return ff_rename(temp_filename, filename, s);
  }
  
  static int init_file(AVFormatContext *s, OutputStream *os, int64_t start_ts)
@@@ -179,27 -94,17 +179,27 @@@ struct variant 
  };
  
  typedef struct HLSContext {
-     AVFormatContext *avfmt;
 +    AVClass *class;
+     AVFormatContext *ctx;
      int n_variants;
      struct variant **variants;
 +    int n_playlists;
 +    struct playlist **playlists;
 +    int n_renditions;
 +    struct rendition **renditions;
 +
      int cur_seq_no;
 -    int end_of_segment;
 +    int live_start_index;
      int first_packet;
      int64_t first_timestamp;
 -    int64_t seek_timestamp;
 -    int seek_flags;
 +    int64_t cur_timestamp;
      AVIOInterruptCB *interrupt_callback;
 +    char *user_agent;                    ///< holds HTTP user agent set as an AVOption to the HTTP protocol context
 +    char *cookies;                       ///< holds HTTP cookie values set in either the initial response or as an AVOption to the HTTP protocol context
 +    char *headers;                       ///< holds HTTP headers set as an AVOption to the HTTP protocol context
 +    char *http_proxy;                    ///< holds the address of the HTTP proxy server
      AVDictionary *avio_opts;
 +    int strict_std_compliance;
  } HLSContext;
  
  static int read_chomp_line(AVIOContext *s, char *buf, int maxlen)
@@@ -619,29 -235,10 +619,29 @@@ static int open_url(HLSContext *c, URLC
  {
      AVDictionary *tmp = NULL;
      int ret;
 +    const char *proto_name = avio_find_protocol_name(url);
 +
 +    if (!proto_name)
 +        return AVERROR_INVALIDDATA;
 +
 +    // only http(s) & file are allowed
 +    if (!av_strstart(proto_name, "http", NULL) && !av_strstart(proto_name, "file", NULL))
 +        return AVERROR_INVALIDDATA;
 +    if (!strncmp(proto_name, url, strlen(proto_name)) && url[strlen(proto_name)] == ':')
 +        ;
 +    else if (strcmp(proto_name, "file") || !strncmp(url, "file,", 5))
 +        return AVERROR_INVALIDDATA;
  
      av_dict_copy(&tmp, c->avio_opts, 0);
 +    av_dict_copy(&tmp, opts, 0);
  
-     ret = ffurl_open_whitelist(uc, url, AVIO_FLAG_READ, c->interrupt_callback, &tmp, c->avfmt->protocol_whitelist);
 -    ret = ffurl_open(uc, url, AVIO_FLAG_READ, c->interrupt_callback, &tmp);
++    ret = ffurl_open_whitelist(uc, url, AVIO_FLAG_READ, c->interrupt_callback, &tmp, c->ctx->protocol_whitelist);
 +    if( ret >= 0) {
 +        // update cookies on http response with setcookies.
 +        URLContext *u = *uc;
 +        update_options(&c->cookies, "cookies", u->priv_data);
 +        av_dict_set(&opts, "cookies", c->cookies, 0);
 +    }
  
      av_dict_free(&tmp);
  
@@@ -657,35 -254,12 +657,34 @@@ static int parse_playlist(HLSContext *c
      uint8_t iv[16] = "";
      int has_iv = 0;
      char key[MAX_URL_SIZE] = "";
 -    char line[1024];
 +    char line[MAX_URL_SIZE];
      const char *ptr;
      int close_in = 0;
 +    int64_t seg_offset = 0;
 +    int64_t seg_size = -1;
      uint8_t *new_url = NULL;
 +    struct variant_info variant_info;
 +    char tmp_str[MAX_URL_SIZE];
 +    struct segment *cur_init_section = NULL;
  
      if (!in) {
-         ret = ffio_open_whitelist(&in, url, AVIO_FLAG_READ,
-                          c->interrupt_callback, &opts, c->avfmt->protocol_whitelist);
 +#if 1
 +        AVDictionary *opts = NULL;
 +        close_in = 1;
 +        /* Some HLS servers don't like being sent the range header */
 +        av_dict_set(&opts, "seekable", "0", 0);
 +
 +        // broker prior HTTP options that should be consistent across requests
 +        av_dict_set(&opts, "user-agent", c->user_agent, 0);
 +        av_dict_set(&opts, "cookies", c->cookies, 0);
 +        av_dict_set(&opts, "headers", c->headers, 0);
 +        av_dict_set(&opts, "http_proxy", c->http_proxy, 0);
 +
++        ret = c->ctx->io_open(c->ctx, &in, url, AVIO_FLAG_READ, &opts);
 +        av_dict_free(&opts);
 +        if (ret < 0)
 +            return ret;
 +#else
          ret = open_in(c, &in, url);
          if (ret < 0)
              return ret;
@@@ -1518,28 -515,8 +1517,28 @@@ static int hls_read_header(AVFormatCont
      HLSContext *c = s->priv_data;
      int ret = 0, i, j, stream_offset = 0;
  
-     c->avfmt = s;
+     c->ctx                = s;
      c->interrupt_callback = &s->interrupt_callback;
 +    c->strict_std_compliance = s->strict_std_compliance;
 +
 +    c->first_packet = 1;
 +    c->first_timestamp = AV_NOPTS_VALUE;
 +    c->cur_timestamp = AV_NOPTS_VALUE;
 +
 +    // if the URL context is good, read important options we must broker later
 +    if (u && u->prot->priv_data_class) {
 +        // get the previous user agent & set back to null if string size is zero
 +        update_options(&c->user_agent, "user-agent", u->priv_data);
 +
 +        // get the previous cookies & set back to null if string size is zero
 +        update_options(&c->cookies, "cookies", u->priv_data);
 +
 +        // get the previous headers & set back to null if string size is zero
 +        update_options(&c->headers, "headers", u->priv_data);
 +
 +        // get the previous http proxt & set back to null if string size is zero
 +        update_options(&c->http_proxy, "http_proxy", u->priv_data);
 +    }
  
      if ((ret = parse_playlist(c, s->filename, NULL, s->pb)) < 0)
          goto fail;
@@@ -70,193 -43,25 +70,191 @@@ typedef struct HLSContext 
      int64_t sequence;
      int64_t start_sequence;
      AVOutputFormat *oformat;
 +    AVOutputFormat *vtt_oformat;
 +
      AVFormatContext *avf;
 +    AVFormatContext *vtt_avf;
 +
      float time;            // Set by a private option.
 -    int  size;             // Set by a private option.
 +    int max_nb_segments;   // Set by a private option.
      int  wrap;             // Set by a private option.
 -    int  version;          // Set by a private option.
 -    int  allowcache;
 +    uint32_t flags;        // enum HLSFlags
 +    char *segment_filename;
 +
 +    int use_localtime;      ///< flag to expand filename with localtime
 +    int allowcache;
      int64_t recording_time;
      int has_video;
 -    // The following timestamps are in AV_TIME_BASE units.
 +    int has_subtitle;
      int64_t start_pts;
      int64_t end_pts;
 -    int64_t duration;      // last segment duration computed so far.
 +    double duration;      // last segment duration computed so far, in seconds
 +    int64_t start_pos;    // last segment starting position
 +    int64_t size;         // last segment size
      int nb_entries;
 -    ListEntry *list;
 -    ListEntry *end_list;
 +    int discontinuity_set;
 +
 +    HLSSegment *segments;
 +    HLSSegment *last_segment;
 +    HLSSegment *old_segments;
 +
      char *basename;
 +    char *vtt_basename;
 +    char *vtt_m3u8_name;
      char *baseurl;
 +    char *format_options_str;
 +    char *vtt_format_options_str;
 +    char *subtitle_filename;
 +    AVDictionary *format_options;
 +
 +    char *key_info_file;
 +    char key_file[LINE_BUFFER_SIZE + 1];
 +    char key_uri[LINE_BUFFER_SIZE + 1];
 +    char key_string[KEYSIZE*2 + 1];
 +    char iv_string[KEYSIZE*2 + 1];
 +    AVDictionary *vtt_format_options;
 +
 +    char *method;
 +
  } HLSContext;
  
-     if ((ret = ffio_open_whitelist(&pb, hls->key_info_file, AVIO_FLAG_READ,
-                            &s->interrupt_callback, NULL, s->protocol_whitelist)) < 0) {
 +static int hls_delete_old_segments(HLSContext *hls) {
 +
 +    HLSSegment *segment, *previous_segment = NULL;
 +    float playlist_duration = 0.0f;
 +    int ret = 0, path_size, sub_path_size;
 +    char *dirname = NULL, *p, *sub_path;
 +    char *path = NULL;
 +
 +    segment = hls->segments;
 +    while (segment) {
 +        playlist_duration += segment->duration;
 +        segment = segment->next;
 +    }
 +
 +    segment = hls->old_segments;
 +    while (segment) {
 +        playlist_duration -= segment->duration;
 +        previous_segment = segment;
 +        segment = previous_segment->next;
 +        if (playlist_duration <= -previous_segment->duration) {
 +            previous_segment->next = NULL;
 +            break;
 +        }
 +    }
 +
 +    if (segment) {
 +        if (hls->segment_filename) {
 +            dirname = av_strdup(hls->segment_filename);
 +        } else {
 +            dirname = av_strdup(hls->avf->filename);
 +        }
 +        if (!dirname) {
 +            ret = AVERROR(ENOMEM);
 +            goto fail;
 +        }
 +        p = (char *)av_basename(dirname);
 +        *p = '\0';
 +    }
 +
 +    while (segment) {
 +        av_log(hls, AV_LOG_DEBUG, "deleting old segment %s\n",
 +                                  segment->filename);
 +        path_size = strlen(dirname) + strlen(segment->filename) + 1;
 +        path = av_malloc(path_size);
 +        if (!path) {
 +            ret = AVERROR(ENOMEM);
 +            goto fail;
 +        }
 +
 +        av_strlcpy(path, dirname, path_size);
 +        av_strlcat(path, segment->filename, path_size);
 +        if (unlink(path) < 0) {
 +            av_log(hls, AV_LOG_ERROR, "failed to delete old segment %s: %s\n",
 +                                     path, strerror(errno));
 +        }
 +
 +        if (segment->sub_filename[0] != '\0') {
 +            sub_path_size = strlen(dirname) + strlen(segment->sub_filename) + 1;
 +            sub_path = av_malloc(sub_path_size);
 +            if (!sub_path) {
 +                ret = AVERROR(ENOMEM);
 +                goto fail;
 +            }
 +
 +            av_strlcpy(sub_path, dirname, sub_path_size);
 +            av_strlcat(sub_path, segment->sub_filename, sub_path_size);
 +            if (unlink(sub_path) < 0) {
 +                av_log(hls, AV_LOG_ERROR, "failed to delete old segment %s: %s\n",
 +                                         sub_path, strerror(errno));
 +            }
 +            av_free(sub_path);
 +        }
 +        av_freep(&path);
 +        previous_segment = segment;
 +        segment = previous_segment->next;
 +        av_free(previous_segment);
 +    }
 +
 +fail:
 +    av_free(path);
 +    av_free(dirname);
 +
 +    return ret;
 +}
 +
 +static int hls_encryption_start(AVFormatContext *s)
 +{
 +    HLSContext *hls = s->priv_data;
 +    int ret;
 +    AVIOContext *pb;
 +    uint8_t key[KEYSIZE];
 +
-     avio_close(pb);
++    if ((ret = s->io_open(s, &pb, hls->key_info_file, AVIO_FLAG_READ, NULL)) < 0) {
 +        av_log(hls, AV_LOG_ERROR,
 +                "error opening key info file %s\n", hls->key_info_file);
 +        return ret;
 +    }
 +
 +    ff_get_line(pb, hls->key_uri, sizeof(hls->key_uri));
 +    hls->key_uri[strcspn(hls->key_uri, "\r\n")] = '\0';
 +
 +    ff_get_line(pb, hls->key_file, sizeof(hls->key_file));
 +    hls->key_file[strcspn(hls->key_file, "\r\n")] = '\0';
 +
 +    ff_get_line(pb, hls->iv_string, sizeof(hls->iv_string));
 +    hls->iv_string[strcspn(hls->iv_string, "\r\n")] = '\0';
 +
-     if ((ret = ffio_open_whitelist(&pb, hls->key_file, AVIO_FLAG_READ,
-                            &s->interrupt_callback, NULL, s->protocol_whitelist)) < 0) {
++    ff_format_io_close(s, &pb);
 +
 +    if (!*hls->key_uri) {
 +        av_log(hls, AV_LOG_ERROR, "no key URI specified in key info file\n");
 +        return AVERROR(EINVAL);
 +    }
 +
 +    if (!*hls->key_file) {
 +        av_log(hls, AV_LOG_ERROR, "no key file specified in key info file\n");
 +        return AVERROR(EINVAL);
 +    }
 +
-     avio_close(pb);
++    if ((ret = s->io_open(s, &pb, hls->key_file, AVIO_FLAG_READ, NULL)) < 0) {
 +        av_log(hls, AV_LOG_ERROR, "error opening key file %s\n", hls->key_file);
 +        return ret;
 +    }
 +
 +    ret = avio_read(pb, key, sizeof(key));
++    ff_format_io_close(s, &pb);
 +    if (ret != sizeof(key)) {
 +        av_log(hls, AV_LOG_ERROR, "error reading key file %s\n", hls->key_file);
 +        if (ret >= 0 || ret == AVERROR_EOF)
 +            ret = AVERROR(EINVAL);
 +        return ret;
 +    }
 +    ff_data_to_hex(hls->key_string, key, sizeof(key), 0);
 +
 +    return 0;
 +}
 +
  static int hls_mux_init(AVFormatContext *s)
  {
      HLSContext *hls = s->priv_data;
  
      oc->oformat            = hls->oformat;
      oc->interrupt_callback = s->interrupt_callback;
 +    oc->max_delay          = s->max_delay;
+     oc->opaque             = s->opaque;
+     oc->io_open            = s->io_open;
+     oc->io_close           = s->io_close;
 +    av_dict_copy(&oc->metadata, s->metadata, 0);
 +
 +    if(hls->vtt_oformat) {
 +        ret = avformat_alloc_output_context2(&hls->vtt_avf, hls->vtt_oformat, NULL, NULL);
 +        if (ret < 0)
 +            return ret;
 +        vtt_oc          = hls->vtt_avf;
 +        vtt_oc->oformat = hls->vtt_oformat;
 +        av_dict_copy(&vtt_oc->metadata, s->metadata, 0);
 +    }
  
      for (i = 0; i < s->nb_streams; i++) {
          AVStream *st;
@@@ -375,38 -135,24 +376,37 @@@ static void set_http_options(AVDictiona
  static int hls_window(AVFormatContext *s, int last)
  {
      HLSContext *hls = s->priv_data;
 -    ListEntry *en;
 -    int64_t target_duration = 0;
 +    HLSSegment *en;
 +    int target_duration = 0;
      int ret = 0;
      AVIOContext *out = NULL;
 +    AVIOContext *sub_out = NULL;
      char temp_filename[1024];
 -    int64_t sequence = FFMAX(hls->start_sequence, hls->sequence - hls->size);
 -
 -    snprintf(temp_filename, sizeof(temp_filename), "%s.tmp", s->filename);
 +    int64_t sequence = FFMAX(hls->start_sequence, hls->sequence - hls->nb_entries);
 +    int version = hls->flags & HLS_SINGLE_FILE ? 4 : 3;
 +    const char *proto = avio_find_protocol_name(s->filename);
 +    int use_rename = proto && !strcmp(proto, "file");
 +    static unsigned warned_non_file;
 +    char *key_uri = NULL;
 +    char *iv_string = NULL;
 +    AVDictionary *options = NULL;
 +
 +    if (!use_rename && !warned_non_file++)
 +        av_log(s, AV_LOG_ERROR, "Cannot use rename on non file protocol, this may lead to races and temporarly partial files\n");
 +
 +    set_http_options(&options, hls);
 +    snprintf(temp_filename, sizeof(temp_filename), use_rename ? "%s.tmp" : "%s", s->filename);
-     if ((ret = ffio_open_whitelist(&out, temp_filename, AVIO_FLAG_WRITE,
-                           &s->interrupt_callback, &options, s->protocol_whitelist)) < 0)
+     if ((ret = s->io_open(s, &out, temp_filename, AVIO_FLAG_WRITE, NULL)) < 0)
          goto fail;
  
 -    for (en = hls->list; en; en = en->next) {
 +    for (en = hls->segments; en; en = en->next) {
          if (target_duration < en->duration)
 -            target_duration = en->duration;
 +            target_duration = ceil(en->duration);
      }
  
 +    hls->discontinuity_set = 0;
      avio_printf(out, "#EXTM3U\n");
 -    avio_printf(out, "#EXT-X-VERSION:%d\n", hls->version);
 +    avio_printf(out, "#EXT-X-VERSION:%d\n", version);
      if (hls->allowcache == 0 || hls->allowcache == 1) {
          avio_printf(out, "#EXT-X-ALLOW-CACHE:%s\n", hls->allowcache == 0 ? "NO" : "YES");
      }
  
      av_log(s, AV_LOG_VERBOSE, "EXT-X-MEDIA-SEQUENCE:%"PRId64"\n",
             sequence);
 -
 -    for (en = hls->list; en; en = en->next) {
 -        if (hls->version > 2)
 -            avio_printf(out, "#EXTINF:%f\n",
 -                        (double)en->duration / AV_TIME_BASE);
 +    if((hls->flags & HLS_DISCONT_START) && sequence==hls->start_sequence && hls->discontinuity_set==0 ){
 +        avio_printf(out, "#EXT-X-DISCONTINUITY\n");
 +        hls->discontinuity_set = 1;
 +    }
 +    for (en = hls->segments; en; en = en->next) {
 +        if (hls->key_info_file && (!key_uri || strcmp(en->key_uri, key_uri) ||
 +                                    av_strcasecmp(en->iv_string, iv_string))) {
 +            avio_printf(out, "#EXT-X-KEY:METHOD=AES-128,URI=\"%s\"", en->key_uri);
 +            if (*en->iv_string)
 +                avio_printf(out, ",IV=0x%s", en->iv_string);
 +            avio_printf(out, "\n");
 +            key_uri = en->key_uri;
 +            iv_string = en->iv_string;
 +        }
 +
 +        if (hls->flags & HLS_ROUND_DURATIONS)
 +            avio_printf(out, "#EXTINF:%ld,\n",  lrint(en->duration));
          else
 -            avio_printf(out, "#EXTINF:%"PRId64",\n",
 -                        av_rescale(en->duration, 1, AV_TIME_BASE));
 +            avio_printf(out, "#EXTINF:%f,\n", en->duration);
 +        if (hls->flags & HLS_SINGLE_FILE)
 +             avio_printf(out, "#EXT-X-BYTERANGE:%"PRIi64"@%"PRIi64"\n",
 +                         en->size, en->pos);
          if (hls->baseurl)
              avio_printf(out, "%s", hls->baseurl);
 -        avio_printf(out, "%s\n", en->name);
 +        avio_printf(out, "%s\n", en->filename);
      }
  
 -    if (last)
 +    if (last && (hls->flags & HLS_OMIT_ENDLIST)==0)
          avio_printf(out, "#EXT-X-ENDLIST\n");
  
-         if ((ret = ffio_open_whitelist(&sub_out, hls->vtt_m3u8_name, AVIO_FLAG_WRITE,
-                           &s->interrupt_callback, &options, s->protocol_whitelist)) < 0)
 +    if( hls->vtt_m3u8_name ) {
++        if ((ret = s->io_open(s, &sub_out, hls->vtt_m3u8_name, AVIO_FLAG_WRITE, &options)) < 0)
 +            goto fail;
 +        avio_printf(sub_out, "#EXTM3U\n");
 +        avio_printf(sub_out, "#EXT-X-VERSION:%d\n", version);
 +        if (hls->allowcache == 0 || hls->allowcache == 1) {
 +            avio_printf(sub_out, "#EXT-X-ALLOW-CACHE:%s\n", hls->allowcache == 0 ? "NO" : "YES");
 +        }
 +        avio_printf(sub_out, "#EXT-X-TARGETDURATION:%d\n", target_duration);
 +        avio_printf(sub_out, "#EXT-X-MEDIA-SEQUENCE:%"PRId64"\n", sequence);
 +
 +        av_log(s, AV_LOG_VERBOSE, "EXT-X-MEDIA-SEQUENCE:%"PRId64"\n",
 +               sequence);
 +
 +        for (en = hls->segments; en; en = en->next) {
 +            avio_printf(sub_out, "#EXTINF:%f,\n", en->duration);
 +            if (hls->flags & HLS_SINGLE_FILE)
 +                 avio_printf(sub_out, "#EXT-X-BYTERANGE:%"PRIi64"@%"PRIi64"\n",
 +                         en->size, en->pos);
 +            if (hls->baseurl)
 +                avio_printf(sub_out, "%s", hls->baseurl);
 +            avio_printf(sub_out, "%s\n", en->sub_filename);
 +        }
 +
 +        if (last)
 +            avio_printf(sub_out, "#EXT-X-ENDLIST\n");
 +
 +    }
 +
  fail:
-     avio_closep(&out);
-     avio_closep(&sub_out);
 +    av_dict_free(&options);
 -    if (ret >= 0)
 -        ff_rename(temp_filename, s->filename);
+     ff_format_io_close(s, &out);
++    ff_format_io_close(s, &sub_out);
 +    if (ret >= 0 && use_rename)
 +        ff_rename(temp_filename, s->filename, s);
      return ret;
  }
  
@@@ -488,101 -190,20 +487,98 @@@ static int hls_start(AVFormatContext *s
  {
      HLSContext *c = s->priv_data;
      AVFormatContext *oc = c->avf;
 +    AVFormatContext *vtt_oc = c->vtt_avf;
 +    AVDictionary *options = NULL;
 +    char *filename, iv_string[KEYSIZE*2 + 1];
      int err = 0;
  
 -    if (av_get_frame_filename(oc->filename, sizeof(oc->filename),
 -                              c->basename, c->wrap ? c->sequence % c->wrap : c->sequence) < 0)
 -        return AVERROR(EINVAL);
 +    if (c->flags & HLS_SINGLE_FILE) {
 +        av_strlcpy(oc->filename, c->basename,
 +                   sizeof(oc->filename));
 +        if (c->vtt_basename)
 +            av_strlcpy(vtt_oc->filename, c->vtt_basename,
 +                  sizeof(vtt_oc->filename));
 +    } else {
 +        if (c->use_localtime) {
 +            time_t now0;
 +            struct tm *tm, tmpbuf;
 +            time(&now0);
 +            tm = localtime_r(&now0, &tmpbuf);
 +            if (!strftime(oc->filename, sizeof(oc->filename), c->basename, tm)) {
 +                av_log(oc, AV_LOG_ERROR, "Could not get segment filename with use_localtime\n");
 +                return AVERROR(EINVAL);
 +            }
 +       } else if (av_get_frame_filename(oc->filename, sizeof(oc->filename),
 +                                  c->basename, c->wrap ? c->sequence % c->wrap : c->sequence) < 0) {
 +            av_log(oc, AV_LOG_ERROR, "Invalid segment filename template '%s' you can try use -use_localtime 1 with it\n", c->basename);
 +            return AVERROR(EINVAL);
 +        }
 +        if( c->vtt_basename) {
 +            if (av_get_frame_filename(vtt_oc->filename, sizeof(vtt_oc->filename),
 +                              c->vtt_basename, c->wrap ? c->sequence % c->wrap : c->sequence) < 0) {
 +                av_log(vtt_oc, AV_LOG_ERROR, "Invalid segment filename template '%s'\n", c->vtt_basename);
 +                return AVERROR(EINVAL);
 +            }
 +       }
 +    }
      c->number++;
  
 -    if ((err = s->io_open(s, &oc->pb, oc->filename, AVIO_FLAG_WRITE, NULL)) < 0)
 -        return err;
 +    set_http_options(&options, c);
 +
 +    if (c->key_info_file) {
 +        if ((err = hls_encryption_start(s)) < 0)
 +            goto fail;
 +        if ((err = av_dict_set(&options, "encryption_key", c->key_string, 0))
 +                < 0)
 +            goto fail;
 +        err = av_strlcpy(iv_string, c->iv_string, sizeof(iv_string));
 +        if (!err)
 +            snprintf(iv_string, sizeof(iv_string), "%032"PRIx64, c->sequence);
 +        if ((err = av_dict_set(&options, "encryption_iv", iv_string, 0)) < 0)
 +           goto fail;
 +
 +        filename = av_asprintf("crypto:%s", oc->filename);
 +        if (!filename) {
 +            err = AVERROR(ENOMEM);
 +            goto fail;
 +        }
-         err = ffio_open_whitelist(&oc->pb, filename, AVIO_FLAG_WRITE,
-                          &s->interrupt_callback, &options, s->protocol_whitelist);
++        err = s->io_open(s, &oc->pb, oc->filename, AVIO_FLAG_WRITE, NULL);
 +        av_free(filename);
 +        av_dict_free(&options);
 +        if (err < 0)
 +            return err;
 +    } else
-         if ((err = ffio_open_whitelist(&oc->pb, oc->filename, AVIO_FLAG_WRITE,
-                           &s->interrupt_callback, &options, s->protocol_whitelist)) < 0)
++        if ((err = s->io_open(s, &oc->pb, oc->filename, AVIO_FLAG_WRITE, &options)) < 0)
 +            goto fail;
 +    if (c->vtt_basename) {
 +        set_http_options(&options, c);
-         if ((err = ffio_open_whitelist(&vtt_oc->pb, vtt_oc->filename, AVIO_FLAG_WRITE,
-                          &s->interrupt_callback, &options, s->protocol_whitelist)) < 0)
++        if ((err = s->io_open(s, &vtt_oc->pb, vtt_oc->filename, AVIO_FLAG_WRITE, &options)) < 0)
 +            goto fail;
 +    }
 +    av_dict_free(&options);
 +
 +    /* We only require one PAT/PMT per segment. */
 +    if (oc->oformat->priv_class && oc->priv_data) {
 +        char period[21];
 +
 +        snprintf(period, sizeof(period), "%d", (INT_MAX / 2) - 1);
  
 -    if (oc->oformat->priv_class && oc->priv_data)
          av_opt_set(oc->priv_data, "mpegts_flags", "resend_headers", 0);
 +        av_opt_set(oc->priv_data, "sdt_period", period, 0);
 +        av_opt_set(oc->priv_data, "pat_period", period, 0);
 +    }
 +
 +    if (c->vtt_basename) {
 +        err = avformat_write_header(vtt_oc,NULL);
 +        if (err < 0)
 +            return err;
 +    }
  
      return 0;
 +fail:
 +    av_dict_free(&options);
 +
 +    return err;
  }
  
  static int hls_write_header(AVFormatContext *s)
@@@ -766,48 -286,28 +762,48 @@@ static int hls_write_packet(AVFormatCon
      if (hls->has_video) {
          can_split = st->codec->codec_type == AVMEDIA_TYPE_VIDEO &&
                      pkt->flags & AV_PKT_FLAG_KEY;
 +        is_ref_pkt = st->codec->codec_type == AVMEDIA_TYPE_VIDEO;
      }
      if (pkt->pts == AV_NOPTS_VALUE)
 -        can_split = 0;
 -    else
 -        hls->duration = pts - hls->end_pts;
 +        is_ref_pkt = can_split = 0;
 +
 +    if (is_ref_pkt)
 +        hls->duration = (double)(pkt->pts - hls->end_pts)
 +                                   * st->time_base.num / st->time_base.den;
 +
 +    if (can_split && av_compare_ts(pkt->pts - hls->start_pts, st->time_base,
 +                                   end_pts, AV_TIME_BASE_Q) >= 0) {
 +        int64_t new_start_pos;
 +        av_write_frame(oc, NULL); /* Flush any buffered data */
  
 -    if (can_split && pts - hls->start_pts >= end_pts) {
 -        ret = append_entry(hls, hls->duration);
 -        if (ret)
 +        new_start_pos = avio_tell(hls->avf->pb);
 +        hls->size = new_start_pos - hls->start_pos;
 +        ret = hls_append_segment(hls, hls->duration, hls->start_pos, hls->size);
 +        hls->start_pos = new_start_pos;
 +        if (ret < 0)
              return ret;
  
 -        hls->end_pts = pts;
 +        hls->end_pts = pkt->pts;
          hls->duration = 0;
  
 -        av_write_frame(oc, NULL); /* Flush any buffered data */
 -        ff_format_io_close(s, &oc->pb);
 +        if (hls->flags & HLS_SINGLE_FILE) {
 +            if (hls->avf->oformat->priv_class && hls->avf->priv_data)
 +                av_opt_set(hls->avf->priv_data, "mpegts_flags", "resend_headers", 0);
 +            hls->number++;
 +        } else {
-             avio_closep(&oc->pb);
++            ff_format_io_close(s, &oc->pb);
 +            if (hls->vtt_avf)
-                 avio_close(hls->vtt_avf->pb);
++                ff_format_io_close(s, &hls->vtt_avf->pb);
  
 -        ret = hls_start(s);
 +            ret = hls_start(s);
 +        }
  
 -        if (ret)
 +        if (ret < 0)
              return ret;
  
 +        if( st->codec->codec_type == AVMEDIA_TYPE_SUBTITLE )
 +            oc = hls->vtt_avf;
 +        else
          oc = hls->avf;
  
          if ((ret = hls_window(s, 0)) < 0)
@@@ -823,35 -323,15 +819,35 @@@ static int hls_write_trailer(struct AVF
  {
      HLSContext *hls = s->priv_data;
      AVFormatContext *oc = hls->avf;
 +    AVFormatContext *vtt_oc = hls->vtt_avf;
  
      av_write_trailer(oc);
 -    ff_format_io_close(s, &oc->pb);
 +    if (oc->pb) {
 +        hls->size = avio_tell(hls->avf->pb) - hls->start_pos;
-         avio_closep(&oc->pb);
++        ff_format_io_close(s, &oc->pb);
 +        hls_append_segment(hls, hls->duration, hls->start_pos, hls->size);
 +    }
 +
 +    if (vtt_oc) {
 +        if (vtt_oc->pb)
 +            av_write_trailer(vtt_oc);
 +        hls->size = avio_tell(hls->vtt_avf->pb) - hls->start_pos;
-         avio_closep(&vtt_oc->pb);
++        ff_format_io_close(s, &vtt_oc->pb);
 +    }
 +    av_freep(&hls->basename);
      avformat_free_context(oc);
 -    av_free(hls->basename);
 -    append_entry(hls, hls->duration);
 +
 +    if (vtt_oc) {
 +        av_freep(&hls->vtt_basename);
 +        av_freep(&hls->vtt_m3u8_name);
 +        avformat_free_context(vtt_oc);
 +    }
 +
 +    hls->avf = NULL;
      hls_window(s, 1);
  
 -    free_entries(hls);
 +    hls_free_segments(hls->segments);
 +    hls_free_segments(hls->old_segments);
      return 0;
  }
  
@@@ -400,15 -237,8 +396,14 @@@ int ff_img_read_packet(AVFormatContext 
                                    s->path,
                                    s->img_number) < 0 && s->img_number > 1)
              return AVERROR(EIO);
 +        }
          for (i = 0; i < 3; i++) {
 -            if (s1->io_open(s1, &f[i], filename, AVIO_FLAG_READ, NULL) < 0) {
 +            if (s1->pb &&
 +                !strcmp(filename_bytes, s->path) &&
 +                !s->loop &&
 +                !s->split_planes) {
 +                f[i] = s1->pb;
-             } else if (open_func(s1, &f[i], filename, AVIO_FLAG_READ,
-                            &s1->interrupt_callback, NULL) < 0) {
++            } else if (s1->io_open(s1, &f[i], filename, AVIO_FLAG_READ, NULL) < 0) {
                  if (i >= 1)
                      break;
                  av_log(s1, AV_LOG_ERROR, "Could not open file : %s\n",
      for (i = 0; i < 3; i++) {
          if (f[i]) {
              ret[i] = avio_read(f[i], pkt->data + pkt->size, size[i]);
 -            if (!s->is_pipe)
 +            if (s->loop && s->is_pipe && ret[i] == AVERROR_EOF) {
 +                if (avio_seek(f[i], 0, SEEK_SET) >= 0) {
 +                    pkt->pos = 0;
 +                    ret[i] = avio_read(f[i], pkt->data + pkt->size, size[i]);
 +                }
 +            }
 +            if (!s->is_pipe && f[i] != s1->pb)
-                 avio_closep(&f[i]);
+                 ff_format_io_close(s1, &f[i]);
              if (ret[i] > 0)
                  pkt->size += ret[i];
          }
      } else {
          s->img_count++;
          s->img_number++;
 +        s->pts++;
          return 0;
      }
-                 avio_closep(&f[i]);
 +
 +fail:
 +    if (!s->is_pipe) {
 +        for (i = 0; i < 3; i++) {
 +            if (f[i] != s1->pb)
++                ff_format_io_close(s1, &f[i]);
 +        }
 +    }
 +    return res;
 +}
 +
 +static int img_read_close(struct AVFormatContext* s1)
 +{
 +#if HAVE_GLOB
 +    VideoDemuxData *s = s1->priv_data;
 +    if (s->use_glob) {
 +        globfree(&s->globstate);
 +    }
 +#endif
 +    return 0;
 +}
 +
 +static int img_read_seek(AVFormatContext *s, int stream_index, int64_t timestamp, int flags)
 +{
 +    VideoDemuxData *s1 = s->priv_data;
 +    AVStream *st = s->streams[0];
 +
 +    if (s1->ts_from_file) {
 +        int index = av_index_search_timestamp(st, timestamp, flags);
 +        if(index < 0)
 +            return -1;
 +        s1->img_number = st->index_entries[index].pos;
 +        return 0;
 +    }
 +
 +    if (timestamp < 0 || !s1->loop && timestamp > s1->img_last - s1->img_first)
 +        return -1;
 +    s1->img_number = timestamp%(s1->img_last - s1->img_first + 1) + s1->img_first;
 +    s1->pts = timestamp;
 +    return 0;
  }
  
  #define OFFSET(x) offsetof(VideoDemuxData, x)
@@@ -104,16 -67,13 +104,15 @@@ static int write_packet(AVFormatContex
          } else if (av_get_frame_filename(filename, sizeof(filename), img->path, img->img_number) < 0 &&
                     img->img_number > 1) {
              av_log(s, AV_LOG_ERROR,
 -                   "Could not get frame filename number %d from pattern '%s'\n",
 +                   "Could not get frame filename number %d from pattern '%s' (either set updatefirst or use a pattern like %%03d within the filename pattern)\n",
                     img->img_number, img->path);
 -            return AVERROR(EIO);
 +            return AVERROR(EINVAL);
          }
 -        for (i = 0; i < 3; i++) {
 -            if (s->io_open(s, &pb[i], img->tmp, AVIO_FLAG_WRITE, NULL) < 0) {
 -                av_log(s, AV_LOG_ERROR, "Could not open file : %s\n", img->tmp);
 +        for (i = 0; i < 4; i++) {
 +            snprintf(img->tmp[i], sizeof(img->tmp[i]), "%s.tmp", filename);
 +            av_strlcpy(img->target[i], filename, sizeof(img->target[i]));
-             if (avio_open2(&pb[i], img->use_rename ? img->tmp[i] : filename, AVIO_FLAG_WRITE,
-                            &s->interrupt_callback, NULL) < 0) {
++            if (s->io_open(s, &pb[i], img->use_rename ? img->tmp[i] : filename, AVIO_FLAG_WRITE, NULL) < 0) {
 +                av_log(s, AV_LOG_ERROR, "Could not open file : %s\n", img->use_rename ? img->tmp[i] : filename);
                  return AVERROR(EIO);
              }
  
          pb[0] = s->pb;
      }
  
 -    if (codec->codec_id == AV_CODEC_ID_RAWVIDEO) {
 +    if (img->split_planes) {
          int ysize = codec->width * codec->height;
 -        avio_write(pb[0], pkt->data, ysize);
 -        avio_write(pb[1], pkt->data + ysize,                           (pkt->size - ysize) / 2);
 -        avio_write(pb[2], pkt->data + ysize + (pkt->size - ysize) / 2, (pkt->size - ysize) / 2);
 +        int usize = AV_CEIL_RSHIFT(codec->width, desc->log2_chroma_w) * AV_CEIL_RSHIFT(codec->height, desc->log2_chroma_h);
 +        if (desc->comp[0].depth >= 9) {
 +            ysize *= 2;
 +            usize *= 2;
 +        }
 +        avio_write(pb[0], pkt->data                , ysize);
 +        avio_write(pb[1], pkt->data + ysize        , usize);
 +        avio_write(pb[2], pkt->data + ysize + usize, usize);
-         avio_closep(&pb[1]);
-         avio_closep(&pb[2]);
+         ff_format_io_close(s, &pb[1]);
+         ff_format_io_close(s, &pb[2]);
 -    } else {
 -        if (ff_guess_image2_codec(s->filename) == AV_CODEC_ID_JPEG2000) {
 -            AVStream *st = s->streams[0];
 -            if (st->codec->extradata_size > 8 &&
 -                AV_RL32(st->codec->extradata + 4) == MKTAG('j', 'p', '2', 'h')) {
 -                if (pkt->size < 8 ||
 -                    AV_RL32(pkt->data + 4) != MKTAG('j', 'p', '2', 'c'))
 -                    goto error;
 -                avio_wb32(pb[0], 12);
 -                ffio_wfourcc(pb[0], "jP  ");
 -                avio_wb32(pb[0], 0x0D0A870A); // signature
 -                avio_wb32(pb[0], 20);
 -                ffio_wfourcc(pb[0], "ftyp");
 -                ffio_wfourcc(pb[0], "jp2 ");
 -                avio_wb32(pb[0], 0);
 -                ffio_wfourcc(pb[0], "jp2 ");
 -                avio_write(pb[0], st->codec->extradata, st->codec->extradata_size);
 -            } else if (pkt->size < 8 ||
 -                       (!st->codec->extradata_size &&
 -                        AV_RL32(pkt->data + 4) != MKTAG('j', 'P', ' ', ' '))) { // signature
 -error:
 -                av_log(s, AV_LOG_ERROR, "malformed JPEG 2000 codestream\n");
 -                return -1;
 -            }
 +        if (desc->nb_components > 3) {
 +            avio_write(pb[3], pkt->data + ysize + 2*usize, ysize);
-             avio_closep(&pb[3]);
++            ff_format_io_close(s, &pb[3]);
          }
 +    } else if (img->muxer) {
 +        int ret;
 +        AVStream *st;
 +        AVPacket pkt2 = {0};
 +        AVFormatContext *fmt = NULL;
 +
 +        av_assert0(!img->split_planes);
 +
 +        ret = avformat_alloc_output_context2(&fmt, NULL, img->muxer, s->filename);
 +        if (ret < 0)
 +            return ret;
 +        st = avformat_new_stream(fmt, NULL);
 +        if (!st) {
 +            avformat_free_context(fmt);
 +            return AVERROR(ENOMEM);
 +        }
 +        st->id = pkt->stream_index;
 +
 +        fmt->pb = pb[0];
 +        if ((ret = av_copy_packet(&pkt2, pkt))                            < 0 ||
 +            (ret = av_dup_packet(&pkt2))                                  < 0 ||
 +            (ret = avcodec_copy_context(st->codec, s->streams[0]->codec)) < 0 ||
 +            (ret = avformat_write_header(fmt, NULL))                      < 0 ||
 +            (ret = av_interleaved_write_frame(fmt, &pkt2))                < 0 ||
 +            (ret = av_write_trailer(fmt))                                 < 0) {
 +            av_packet_unref(&pkt2);
 +            avformat_free_context(fmt);
 +            return ret;
 +        }
 +        av_packet_unref(&pkt2);
 +        avformat_free_context(fmt);
 +    } else {
          avio_write(pb[0], pkt->data, pkt->size);
      }
      avio_flush(pb[0]);
      if (!img->is_pipe) {
-         avio_closep(&pb[0]);
+         ff_format_io_close(s, &pb[0]);
 -        ff_rename(img->tmp, filename);
 +        for (i = 0; i < nb_renames; i++) {
 +            ff_rename(img->tmp[i], img->target[i], s);
 +        }
      }
  
      img->img_number++;
@@@ -494,58 -420,9 +494,64 @@@ static inline int ff_rename(const char 
  }
  
  /**
 + * Allocate extradata with additional AV_INPUT_BUFFER_PADDING_SIZE at end
 + * which is always set to 0.
 + *
 + * @param size size of extradata
 + * @return 0 if OK, AVERROR_xxx on error
 + */
 +int ff_alloc_extradata(AVCodecContext *avctx, int size);
 +
 +/**
 + * Allocate extradata with additional AV_INPUT_BUFFER_PADDING_SIZE at end
 + * which is always set to 0 and fill it from pb.
 + *
 + * @param size size of extradata
 + * @return >= 0 if OK, AVERROR_xxx on error
 + */
 +int ff_get_extradata(AVCodecContext *avctx, AVIOContext *pb, int size);
 +
 +/**
 + * add frame for rfps calculation.
 + *
 + * @param dts timestamp of the i-th frame
 + * @return 0 if OK, AVERROR_xxx on error
 + */
 +int ff_rfps_add_frame(AVFormatContext *ic, AVStream *st, int64_t dts);
 +
 +void ff_rfps_calculate(AVFormatContext *ic);
 +
 +/**
 + * Flags for AVFormatContext.write_uncoded_frame()
 + */
 +enum AVWriteUncodedFrameFlags {
 +
 +    /**
 +     * Query whether the feature is possible on this stream.
 +     * The frame argument is ignored.
 +     */
 +    AV_WRITE_UNCODED_FRAME_QUERY           = 0x0001,
 +
 +};
 +
 +/**
 + * Copies the whilelists from one context to the other
 + */
 +int ff_copy_whitelists(AVFormatContext *dst, AVFormatContext *src);
 +
 +int ffio_open2_wrapper(struct AVFormatContext *s, AVIOContext **pb, const char *url, int flags,
 +                       const AVIOInterruptCB *int_cb, AVDictionary **options);
 +
 +/**
 + * Returned by demuxers to indicate that data was consumed but discarded
 + * (ignored streams or junk data). The framework will re-call the demuxer.
 + */
 +#define FFERROR_REDO FFERRTAG('R','E','D','O')
 +
++/*
+  * A wrapper around AVFormatContext.io_close that should be used
+  * intead of calling the pointer directly.
+  */
+ void ff_format_io_close(AVFormatContext *s, AVIOContext **pb);
  #endif /* AVFORMAT_INTERNAL_H */
index c003eab,0000000..288b2a1
mode 100644,000000..100644
--- /dev/null
@@@ -1,482 -1,0 +1,478 @@@
-         AVOpenCallback open_func = avctx->open_cb;
 +/*
 + * Magic Lantern Video (MLV) demuxer
 + * Copyright (c) 2014 Peter Ross
 + *
 + * 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
 + */
 +
 +/**
 + * @file
 + * Magic Lantern Video (MLV) demuxer
 + */
 +
 +#include "libavutil/eval.h"
 +#include "libavutil/imgutils.h"
 +#include "libavutil/intreadwrite.h"
 +#include "libavutil/rational.h"
 +#include "avformat.h"
 +#include "avio_internal.h"
 +#include "internal.h"
 +#include "riff.h"
 +
 +#define MLV_VERSION "v2.0"
 +
 +#define MLV_VIDEO_CLASS_RAW  1
 +#define MLV_VIDEO_CLASS_YUV  2
 +#define MLV_VIDEO_CLASS_JPEG 3
 +#define MLV_VIDEO_CLASS_H264 4
 +
 +#define MLV_AUDIO_CLASS_WAV  1
 +
 +#define MLV_CLASS_FLAG_DELTA 0x40
 +#define MLV_CLASS_FLAG_LZMA  0x80
 +
 +typedef struct {
 +    AVIOContext *pb[101];
 +    int class[2];
 +    int stream_index;
 +    uint64_t pts;
 +} MlvContext;
 +
 +static int probe(AVProbeData *p)
 +{
 +    if (AV_RL32(p->buf) == MKTAG('M','L','V','I') &&
 +        AV_RL32(p->buf + 4) >= 52 &&
 +        !memcmp(p->buf + 8, MLV_VERSION, 5))
 +        return AVPROBE_SCORE_MAX;
 +    return 0;
 +}
 +
 +static int check_file_header(AVIOContext *pb, uint64_t guid)
 +{
 +    unsigned int size;
 +    uint8_t version[8];
 +
 +    avio_skip(pb, 4);
 +    size = avio_rl32(pb);
 +    if (size < 52)
 +        return AVERROR_INVALIDDATA;
 +    avio_read(pb, version, 8);
 +    if (memcmp(version, MLV_VERSION, 5) || avio_rl64(pb) != guid)
 +        return AVERROR_INVALIDDATA;
 +    avio_skip(pb, size - 24);
 +    return 0;
 +}
 +
 +static void read_string(AVFormatContext *avctx, AVIOContext *pb, const char *tag, int size)
 +{
 +    char * value = av_malloc(size + 1);
 +    if (!value) {
 +        avio_skip(pb, size);
 +        return;
 +    }
 +
 +    avio_read(pb, value, size);
 +    if (!value[0]) {
 +        av_free(value);
 +        return;
 +    }
 +
 +    value[size] = 0;
 +    av_dict_set(&avctx->metadata, tag, value, AV_DICT_DONT_STRDUP_VAL);
 +}
 +
 +static void read_uint8(AVFormatContext *avctx, AVIOContext *pb, const char *tag, const char *fmt)
 +{
 +    av_dict_set_int(&avctx->metadata, tag, avio_r8(pb), 0);
 +}
 +
 +static void read_uint16(AVFormatContext *avctx, AVIOContext *pb, const char *tag, const char *fmt)
 +{
 +    av_dict_set_int(&avctx->metadata, tag, avio_rl16(pb), 0);
 +}
 +
 +static void read_uint32(AVFormatContext *avctx, AVIOContext *pb, const char *tag, const char *fmt)
 +{
 +    av_dict_set_int(&avctx->metadata, tag, avio_rl32(pb), 0);
 +}
 +
 +static void read_uint64(AVFormatContext *avctx, AVIOContext *pb, const char *tag, const char *fmt)
 +{
 +    av_dict_set_int(&avctx->metadata, tag, avio_rl64(pb), 0);
 +}
 +
 +static int scan_file(AVFormatContext *avctx, AVStream *vst, AVStream *ast, int file)
 +{
 +    MlvContext *mlv = avctx->priv_data;
 +    AVIOContext *pb = mlv->pb[file];
 +    int ret;
 +    while (!avio_feof(pb)) {
 +        int type;
 +        unsigned int size;
 +        type = avio_rl32(pb);
 +        size = avio_rl32(pb);
 +        avio_skip(pb, 8); //timestamp
 +        if (size < 16)
 +            break;
 +        size -= 16;
 +        if (vst && type == MKTAG('R','A','W','I') && size >= 164) {
 +            vst->codec->width  = avio_rl16(pb);
 +            vst->codec->height = avio_rl16(pb);
 +            ret = av_image_check_size(vst->codec->width, vst->codec->height, 0, avctx);
 +            if (ret < 0)
 +                return ret;
 +            if (avio_rl32(pb) != 1)
 +                avpriv_request_sample(avctx, "raw api version");
 +            avio_skip(pb, 20); // pointer, width, height, pitch, frame_size
 +            vst->codec->bits_per_coded_sample = avio_rl32(pb);
 +            if (vst->codec->bits_per_coded_sample < 0 ||
 +                vst->codec->bits_per_coded_sample > (INT_MAX - 7) / (vst->codec->width * vst->codec->height)) {
 +                av_log(avctx, AV_LOG_ERROR,
 +                       "invalid bits_per_coded_sample %d (size: %dx%d)\n",
 +                       vst->codec->bits_per_coded_sample,
 +                       vst->codec->width, vst->codec->height);
 +                return AVERROR_INVALIDDATA;
 +            }
 +            avio_skip(pb, 8 + 16 + 24); // black_level, white_level, xywh, active_area, exposure_bias
 +            if (avio_rl32(pb) != 0x2010100) /* RGGB */
 +                avpriv_request_sample(avctx, "cfa_pattern");
 +            avio_skip(pb, 80); // calibration_illuminant1, color_matrix1, dynamic_range
 +            vst->codec->pix_fmt  = AV_PIX_FMT_BAYER_RGGB16LE;
 +            vst->codec->codec_tag = MKTAG('B', 'I', 'T', 16);
 +            size -= 164;
 +        } else if (ast && type == MKTAG('W', 'A', 'V', 'I') && size >= 16) {
 +            ret = ff_get_wav_header(avctx, pb, ast->codec, 16, 0);
 +            if (ret < 0)
 +                return ret;
 +            size -= 16;
 +        } else if (type == MKTAG('I','N','F','O')) {
 +            if (size > 0)
 +                read_string(avctx, pb, "info", size);
 +            continue;
 +        } else if (type == MKTAG('I','D','N','T') && size >= 36) {
 +            read_string(avctx, pb, "cameraName", 32);
 +            read_uint32(avctx, pb, "cameraModel", "0x%"PRIx32);
 +            size -= 36;
 +            if (size >= 32) {
 +                read_string(avctx, pb, "cameraSerial", 32);
 +                size -= 32;
 +            }
 +        } else if (type == MKTAG('L','E','N','S') && size >= 48) {
 +            read_uint16(avctx, pb, "focalLength", "%i");
 +            read_uint16(avctx, pb, "focalDist", "%i");
 +            read_uint16(avctx, pb, "aperture", "%i");
 +            read_uint8(avctx, pb, "stabilizerMode", "%i");
 +            read_uint8(avctx, pb, "autofocusMode", "%i");
 +            read_uint32(avctx, pb, "flags", "0x%"PRIx32);
 +            read_uint32(avctx, pb, "lensID", "%"PRIi32);
 +            read_string(avctx, pb, "lensName", 32);
 +            size -= 48;
 +            if (size >= 32) {
 +                read_string(avctx, pb, "lensSerial", 32);
 +                size -= 32;
 +            }
 +        } else if (vst && type == MKTAG('V', 'I', 'D', 'F') && size >= 4) {
 +            uint64_t pts = avio_rl32(pb);
 +            ff_add_index_entry(&vst->index_entries, &vst->nb_index_entries, &vst->index_entries_allocated_size,
 +                               avio_tell(pb) - 20, pts, file, 0, AVINDEX_KEYFRAME);
 +            size -= 4;
 +        } else if (ast && type == MKTAG('A', 'U', 'D', 'F') && size >= 4) {
 +            uint64_t pts = avio_rl32(pb);
 +            ff_add_index_entry(&ast->index_entries, &ast->nb_index_entries, &ast->index_entries_allocated_size,
 +                               avio_tell(pb) - 20, pts, file, 0, AVINDEX_KEYFRAME);
 +            size -= 4;
 +        } else if (vst && type == MKTAG('W','B','A','L') && size >= 28) {
 +            read_uint32(avctx, pb, "wb_mode", "%"PRIi32);
 +            read_uint32(avctx, pb, "kelvin", "%"PRIi32);
 +            read_uint32(avctx, pb, "wbgain_r", "%"PRIi32);
 +            read_uint32(avctx, pb, "wbgain_g", "%"PRIi32);
 +            read_uint32(avctx, pb, "wbgain_b", "%"PRIi32);
 +            read_uint32(avctx, pb, "wbs_gm", "%"PRIi32);
 +            read_uint32(avctx, pb, "wbs_ba", "%"PRIi32);
 +            size -= 28;
 +        } else if (type == MKTAG('R','T','C','I') && size >= 20) {
 +            char str[32];
 +            struct tm time = { 0 };
 +            time.tm_sec    = avio_rl16(pb);
 +            time.tm_min    = avio_rl16(pb);
 +            time.tm_hour   = avio_rl16(pb);
 +            time.tm_mday   = avio_rl16(pb);
 +            time.tm_mon    = avio_rl16(pb);
 +            time.tm_year   = avio_rl16(pb);
 +            time.tm_wday   = avio_rl16(pb);
 +            time.tm_yday   = avio_rl16(pb);
 +            time.tm_isdst  = avio_rl16(pb);
 +            avio_skip(pb, 2);
 +            if (strftime(str, sizeof(str), "%Y-%m-%d %H:%M:%S", &time))
 +                av_dict_set(&avctx->metadata, "time", str, 0);
 +            size -= 20;
 +        } else if (type == MKTAG('E','X','P','O') && size >= 16) {
 +            av_dict_set(&avctx->metadata, "isoMode", avio_rl32(pb) ? "auto" : "manual", 0);
 +            read_uint32(avctx, pb, "isoValue", "%"PRIi32);
 +            read_uint32(avctx, pb, "isoAnalog", "%"PRIi32);
 +            read_uint32(avctx, pb, "digitalGain", "%"PRIi32);
 +            size -= 16;
 +            if (size >= 8) {
 +                read_uint64(avctx, pb, "shutterValue", "%"PRIi64);
 +                size -= 8;
 +            }
 +        } else if (type == MKTAG('S','T','Y','L') && size >= 36) {
 +            read_uint32(avctx, pb, "picStyleId", "%"PRIi32);
 +            read_uint32(avctx, pb, "contrast", "%"PRIi32);
 +            read_uint32(avctx, pb, "sharpness", "%"PRIi32);
 +            read_uint32(avctx, pb, "saturation", "%"PRIi32);
 +            read_uint32(avctx, pb, "colortone", "%"PRIi32);
 +            read_string(avctx, pb, "picStyleName", 16);
 +            size -= 36;
 +        } else if (type == MKTAG('M','A','R','K')) {
 +        } else if (type == MKTAG('N','U','L','L')) {
 +        } else if (type == MKTAG('M','L','V','I')) { /* occurs when MLV and Mnn files are concatenated */
 +        } else {
 +            av_log(avctx, AV_LOG_INFO, "unsupported tag %c%c%c%c, size %u\n", type&0xFF, (type>>8)&0xFF, (type>>16)&0xFF, (type>>24)&0xFF, size);
 +        }
 +        avio_skip(pb, size);
 +    }
 +    return 0;
 +}
 +
 +static int read_header(AVFormatContext *avctx)
 +{
 +    MlvContext *mlv = avctx->priv_data;
 +    AVIOContext *pb = avctx->pb;
 +    AVStream *vst = NULL, *ast = NULL;
 +    int size, ret;
 +    unsigned nb_video_frames, nb_audio_frames;
 +    uint64_t guid;
 +    char guidstr[32];
 +
 +    avio_skip(pb, 4);
 +    size = avio_rl32(pb);
 +    if (size < 52)
 +        return AVERROR_INVALIDDATA;
 +
 +    avio_skip(pb, 8);
 +
 +    guid = avio_rl64(pb);
 +    snprintf(guidstr, sizeof(guidstr), "0x%"PRIx64, guid);
 +    av_dict_set(&avctx->metadata, "guid", guidstr, 0);
 +
 +    avio_skip(pb, 8); //fileNum, fileCount, fileFlags
 +
 +    mlv->class[0] = avio_rl16(pb);
 +    mlv->class[1] = avio_rl16(pb);
 +
 +    nb_video_frames = avio_rl32(pb);
 +    nb_audio_frames = avio_rl32(pb);
 +
 +    if (nb_video_frames && mlv->class[0]) {
 +        vst = avformat_new_stream(avctx, NULL);
 +        if (!vst)
 +            return AVERROR(ENOMEM);
 +        vst->id = 0;
 +        vst->nb_frames = nb_video_frames;
 +        if ((mlv->class[0] & (MLV_CLASS_FLAG_DELTA|MLV_CLASS_FLAG_LZMA)))
 +            avpriv_request_sample(avctx, "compression");
 +        vst->codec->codec_type = AVMEDIA_TYPE_VIDEO;
 +        switch (mlv->class[0] & ~(MLV_CLASS_FLAG_DELTA|MLV_CLASS_FLAG_LZMA)) {
 +        case MLV_VIDEO_CLASS_RAW:
 +            vst->codec->codec_id = AV_CODEC_ID_RAWVIDEO;
 +            break;
 +        case MLV_VIDEO_CLASS_YUV:
 +            vst->codec->pix_fmt  = AV_PIX_FMT_YUV420P;
 +            vst->codec->codec_id = AV_CODEC_ID_RAWVIDEO;
 +            vst->codec->codec_tag = 0;
 +            break;
 +        case MLV_VIDEO_CLASS_JPEG:
 +            vst->codec->codec_id = AV_CODEC_ID_MJPEG;
 +            vst->codec->codec_tag = 0;
 +            break;
 +        case MLV_VIDEO_CLASS_H264:
 +            vst->codec->codec_id = AV_CODEC_ID_H264;
 +            vst->codec->codec_tag = 0;
 +            break;
 +        default:
 +            avpriv_request_sample(avctx, "unknown video class");
 +        }
 +    }
 +
 +    if (nb_audio_frames && mlv->class[1]) {
 +        ast = avformat_new_stream(avctx, NULL);
 +        if (!ast)
 +            return AVERROR(ENOMEM);
 +        ast->id = 1;
 +        ast->nb_frames = nb_audio_frames;
 +        if ((mlv->class[1] & MLV_CLASS_FLAG_LZMA))
 +            avpriv_request_sample(avctx, "compression");
 +        if ((mlv->class[1] & ~MLV_CLASS_FLAG_LZMA) != MLV_AUDIO_CLASS_WAV)
 +            avpriv_request_sample(avctx, "unknown audio class");
 +
 +        ast->codec->codec_type = AVMEDIA_TYPE_AUDIO;
 +        avpriv_set_pts_info(ast, 33, 1, ast->codec->sample_rate);
 +    }
 +
 +    if (vst) {
 +       AVRational framerate;
 +       framerate.num = avio_rl32(pb);
 +       framerate.den = avio_rl32(pb);
 +       avpriv_set_pts_info(vst, 64, framerate.den, framerate.num);
 +    } else
 +       avio_skip(pb, 8);
 +
 +    avio_skip(pb, size - 52);
 +
 +    /* scan primary file */
 +    mlv->pb[100] = avctx->pb;
 +    ret = scan_file(avctx, vst, ast, 100);
 +    if (ret < 0)
 +        return ret;
 +
 +    /* scan secondary files */
 +    if (strlen(avctx->filename) > 2) {
 +        int i;
 +        char *filename = av_strdup(avctx->filename);
-         if (!open_func)
-             open_func = ffio_open2_wrapper;
 +
 +        if (!filename)
 +            return AVERROR(ENOMEM);
 +
-             if (open_func(avctx, &mlv->pb[i], filename, AVIO_FLAG_READ, &avctx->interrupt_callback, NULL) < 0)
 +        for (i = 0; i < 100; i++) {
 +            snprintf(filename + strlen(filename) - 2, 3, "%02d", i);
-                 avio_closep(&mlv->pb[i]);
++            if (avctx->io_open(avctx, &mlv->pb[i], filename, AVIO_FLAG_READ, NULL) < 0)
 +                break;
 +            if (check_file_header(mlv->pb[i], guid) < 0) {
 +                av_log(avctx, AV_LOG_WARNING, "ignoring %s; bad format or guid mismatch\n", filename);
-                 avio_closep(&mlv->pb[i]);
++                ff_format_io_close(avctx, &mlv->pb[i]);
 +                continue;
 +            }
 +            av_log(avctx, AV_LOG_INFO, "scanning %s\n", filename);
 +            ret = scan_file(avctx, vst, ast, i);
 +            if (ret < 0) {
 +                av_log(avctx, AV_LOG_WARNING, "ignoring %s; %s\n", filename, av_err2str(ret));
-             avio_closep(&mlv->pb[i]);
++                ff_format_io_close(avctx, &mlv->pb[i]);
 +                continue;
 +            }
 +        }
 +        av_free(filename);
 +    }
 +
 +    if (vst)
 +        vst->duration = vst->nb_index_entries;
 +    if (ast)
 +        ast->duration = ast->nb_index_entries;
 +
 +    if ((vst && !vst->nb_index_entries) || (ast && !ast->nb_index_entries)) {
 +        av_log(avctx, AV_LOG_ERROR, "no index entries found\n");
 +        return AVERROR_INVALIDDATA;
 +    }
 +
 +    if (vst && ast)
 +        avio_seek(pb, FFMIN(vst->index_entries[0].pos, ast->index_entries[0].pos), SEEK_SET);
 +    else if (vst)
 +        avio_seek(pb, vst->index_entries[0].pos, SEEK_SET);
 +    else if (ast)
 +        avio_seek(pb, ast->index_entries[0].pos, SEEK_SET);
 +
 +    return 0;
 +}
 +
 +static int read_packet(AVFormatContext *avctx, AVPacket *pkt)
 +{
 +    MlvContext *mlv = avctx->priv_data;
 +    AVIOContext *pb;
 +    AVStream *st = avctx->streams[mlv->stream_index];
 +    int index, ret;
 +    unsigned int size, space;
 +
 +    if (mlv->pts >= st->duration)
 +        return AVERROR_EOF;
 +
 +    index = av_index_search_timestamp(st, mlv->pts, AVSEEK_FLAG_ANY);
 +    if (index < 0) {
 +        av_log(avctx, AV_LOG_ERROR, "could not find index entry for frame %"PRId64"\n", mlv->pts);
 +        return AVERROR(EIO);
 +    }
 +
 +    pb = mlv->pb[st->index_entries[index].size];
 +    avio_seek(pb, st->index_entries[index].pos, SEEK_SET);
 +
 +    avio_skip(pb, 4); // blockType
 +    size = avio_rl32(pb);
 +    if (size < 16)
 +        return AVERROR_INVALIDDATA;
 +    avio_skip(pb, 12); //timestamp, frameNumber
 +    if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO)
 +        avio_skip(pb, 8); // cropPosX, cropPosY, panPosX, panPosY
 +    space = avio_rl32(pb);
 +    avio_skip(pb, space);
 +
 +    if ((mlv->class[st->id] & (MLV_CLASS_FLAG_DELTA|MLV_CLASS_FLAG_LZMA))) {
 +        ret = AVERROR_PATCHWELCOME;
 +    } else if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
 +        ret = av_get_packet(pb, pkt, (st->codec->width * st->codec->height * st->codec->bits_per_coded_sample + 7) >> 3);
 +    } else { // AVMEDIA_TYPE_AUDIO
 +        if (space > UINT_MAX - 24 || size < (24 + space))
 +            return AVERROR_INVALIDDATA;
 +        ret = av_get_packet(pb, pkt, size - (24 + space));
 +    }
 +
 +    if (ret < 0)
 +        return ret;
 +
 +    pkt->stream_index = mlv->stream_index;
 +    pkt->pts = mlv->pts;
 +
 +    mlv->stream_index++;
 +    if (mlv->stream_index == avctx->nb_streams) {
 +        mlv->stream_index = 0;
 +        mlv->pts++;
 +    }
 +    return 0;
 +}
 +
 +static int read_seek(AVFormatContext *avctx, int stream_index, int64_t timestamp, int flags)
 +{
 +    MlvContext *mlv = avctx->priv_data;
 +
 +    if ((flags & AVSEEK_FLAG_FRAME) || (flags & AVSEEK_FLAG_BYTE))
 +        return AVERROR(ENOSYS);
 +
 +    if (!avctx->pb->seekable)
 +        return AVERROR(EIO);
 +
 +    mlv->pts = timestamp;
 +    return 0;
 +}
 +
 +static int read_close(AVFormatContext *s)
 +{
 +    MlvContext *mlv = s->priv_data;
 +    int i;
 +    for (i = 0; i < 100; i++)
 +        if (mlv->pb[i])
++            ff_format_io_close(s, &mlv->pb[i]);
 +    return 0;
 +}
 +
 +AVInputFormat ff_mlv_demuxer = {
 +    .name           = "mlv",
 +    .long_name      = NULL_IF_CONFIG_SMALL("Magic Lantern Video (MLV)"),
 +    .priv_data_size = sizeof(MlvContext),
 +    .read_probe     = probe,
 +    .read_header    = read_header,
 +    .read_packet    = read_packet,
 +    .read_close     = read_close,
 +    .read_seek      = read_seek,
 +};
@@@ -2893,43 -2400,9 +2893,37 @@@ static void mov_build_index(MOVContext 
      }
  }
  
 -static int mov_open_dref(AVFormatContext *s, AVIOContext **pb, char *src,
 -                         MOVDref *ref)
 +static int test_same_origin(const char *src, const char *ref) {
 +    char src_proto[64];
 +    char ref_proto[64];
 +    char src_auth[256];
 +    char ref_auth[256];
 +    char src_host[256];
 +    char ref_host[256];
 +    int src_port=-1;
 +    int ref_port=-1;
 +
 +    av_url_split(src_proto, sizeof(src_proto), src_auth, sizeof(src_auth), src_host, sizeof(src_host), &src_port, NULL, 0, src);
 +    av_url_split(ref_proto, sizeof(ref_proto), ref_auth, sizeof(ref_auth), ref_host, sizeof(ref_host), &ref_port, NULL, 0, ref);
 +
 +    if (strlen(src) == 0) {
 +        return -1;
 +    } else if (strlen(src_auth) + 1 >= sizeof(src_auth) ||
 +        strlen(ref_auth) + 1 >= sizeof(ref_auth) ||
 +        strlen(src_host) + 1 >= sizeof(src_host) ||
 +        strlen(ref_host) + 1 >= sizeof(ref_host)) {
 +        return 0;
 +    } else if (strcmp(src_proto, ref_proto) ||
 +               strcmp(src_auth, ref_auth) ||
 +               strcmp(src_host, ref_host) ||
 +               src_port != ref_port) {
 +        return 0;
 +    } else
 +        return 1;
 +}
 +
- static int mov_open_dref(MOVContext *c, AVIOContext **pb, const char *src, MOVDref *ref,
-                          AVIOInterruptCB *int_cb)
++static int mov_open_dref(MOVContext *c, AVIOContext **pb, const char *src, MOVDref *ref)
  {
-     AVOpenCallback open_func = c->fc->open_cb;
-     if (!open_func)
-         open_func = ffio_open2_wrapper;
      /* try relative path, we do not try the absolute because it can leak information about our
         system to an attacker */
      if (ref->nlvl_to > 0 && ref->nlvl_from > 0) {
              filename[src_path - src] = 0;
  
              for (i = 1; i < ref->nlvl_from; i++)
 -                av_strlcat(filename, "../", 1024);
 +                av_strlcat(filename, "../", sizeof(filename));
 +
 +            av_strlcat(filename, ref->path + l + 1, sizeof(filename));
-             if (!c->use_absolute_path && !c->fc->open_cb) {
++            if (!c->use_absolute_path) {
 +                int same_origin = test_same_origin(src, filename);
 +
 +                if (!same_origin) {
 +                    av_log(c->fc, AV_LOG_ERROR,
 +                        "Reference with mismatching origin, %s not tried for security reasons, "
 +                        "set demuxer option use_absolute_path to allow it anyway\n",
 +                        ref->path);
 +                    return AVERROR(ENOENT);
 +                }
  
 -            av_strlcat(filename, ref->path + l + 1, 1024);
 +                if(strstr(ref->path + l + 1, "..") ||
 +                   strstr(ref->path + l + 1, ":") ||
 +                   (ref->nlvl_from > 1 && same_origin < 0) ||
 +                   (filename[0] == '/' && src_path == src))
 +                    return AVERROR(ENOENT);
 +            }
  
 -            if (!s->io_open(s, pb, filename, AVIO_FLAG_READ, NULL))
 +            if (strlen(filename) + 1 == sizeof(filename))
 +                return AVERROR(ENOENT);
-             if (!open_func(c->fc, pb, filename, AVIO_FLAG_READ, int_cb, NULL))
++            if (!c->fc->io_open(c->fc, pb, filename, AVIO_FLAG_READ, NULL))
                  return 0;
          }
-         if (!open_func(c->fc, pb, ref->path, AVIO_FLAG_READ, int_cb, NULL))
-             return 0;
-     } else if (c->fc->open_cb) {
-         if (!open_func(c->fc, pb, ref->path, AVIO_FLAG_READ, int_cb, NULL))
 +    } else if (c->use_absolute_path) {
 +        av_log(c->fc, AV_LOG_WARNING, "Using absolute path on user request, "
 +               "this is a possible security issue\n");
++        if (!c->fc->io_open(c->fc, pb, ref->path, AVIO_FLAG_READ, NULL))
 +            return 0;
 +    } else {
 +        av_log(c->fc, AV_LOG_ERROR,
 +               "Absolute path %s not tried for security reasons, "
 +               "set demuxer option use_absolute_path to allow absolute paths\n",
 +               ref->path);
      }
  
      return AVERROR(ENOENT);
@@@ -3052,8 -2485,7 +3043,7 @@@ static int mov_read_trak(MOVContext *c
      if (sc->dref_id-1 < sc->drefs_count && sc->drefs[sc->dref_id-1].path) {
          MOVDref *dref = &sc->drefs[sc->dref_id - 1];
          if (c->enable_drefs) {
-             if (mov_open_dref(c, &sc->pb, c->fc->filename, dref,
-                               &c->fc->interrupt_callback) < 0)
 -            if (mov_open_dref(c->fc, &sc->pb, c->fc->filename, dref) < 0)
++            if (mov_open_dref(c, &sc->pb, c->fc->filename, dref) < 0)
                  av_log(c->fc, AV_LOG_ERROR,
                         "stream %d, error opening alias: path='%s', dir='%s', "
                         "filename='%s', volume='%s', nlvl_from=%d, nlvl_to=%d\n",
@@@ -4579,13 -3334,9 +4569,13 @@@ static int mov_read_close(AVFormatConte
              av_freep(&sc->drefs[j].dir);
          }
          av_freep(&sc->drefs);
 -        if (sc->pb && sc->pb != s->pb)
 +
 +        sc->drefs_count = 0;
 +
 +        if (!sc->pb_is_copied)
-             avio_closep(&sc->pb);
+             ff_format_io_close(s, &sc->pb);
  
 +        sc->pb = NULL;
          av_freep(&sc->chunk_offsets);
          av_freep(&sc->stsc_data);
          av_freep(&sc->sample_sizes);
Simple merge
@@@ -95,10 -88,19 +95,28 @@@ static const AVClass av_format_context_
      .version        = LIBAVUTIL_VERSION_INT,
      .child_next     = format_child_next,
      .child_class_next = format_child_class_next,
 +    .category       = AV_CLASS_CATEGORY_MUXER,
 +    .get_category   = get_category,
  };
  
 -    return avio_open2(pb, url, flags, &s->interrupt_callback, options);
+ static int io_open_default(AVFormatContext *s, AVIOContext **pb,
+                            const char *url, int flags, AVDictionary **options)
+ {
++#if FF_API_OLD_OPEN_CALLBACKS
++FF_DISABLE_DEPRECATION_WARNINGS
++    if (s->open_cb)
++        return s->open_cb(s, pb, url, flags, &s->interrupt_callback, options);
++FF_ENABLE_DEPRECATION_WARNINGS
++#endif
++
++    return ffio_open_whitelist(pb, url, flags, &s->interrupt_callback, options, s->protocol_whitelist);
+ }
+ static void io_close_default(AVFormatContext *s, AVIOContext *pb)
+ {
+     avio_close(pb);
+ }
  static void avformat_get_context_defaults(AVFormatContext *s)
  {
      memset(s, 0, sizeof(AVFormatContext));
@@@ -146,16 -60,16 +146,19 @@@ static int segment_mux_init(AVFormatCon
      SegmentContext *seg = s->priv_data;
      AVFormatContext *oc;
      int i;
 +    int ret;
  
 -    seg->avf = oc = avformat_alloc_context();
 -    if (!oc)
 -        return AVERROR(ENOMEM);
 +    ret = avformat_alloc_output_context2(&seg->avf, seg->oformat, NULL, NULL);
 +    if (ret < 0)
 +        return ret;
 +    oc = seg->avf;
  
 -    oc->oformat            = seg->oformat;
      oc->interrupt_callback = s->interrupt_callback;
 +    oc->max_delay          = s->max_delay;
 +    av_dict_copy(&oc->metadata, s->metadata, 0);
+     oc->opaque             = s->opaque;
+     oc->io_close           = s->io_close;
+     oc->io_open            = s->io_open;
  
      for (i = 0; i < s->nb_streams; i++) {
          AVStream *st;
@@@ -227,29 -131,24 +230,28 @@@ static int segment_start(AVFormatContex
  
      if (write_header) {
          avformat_free_context(oc);
 -        c->avf = NULL;
 +        seg->avf = NULL;
          if ((err = segment_mux_init(s)) < 0)
              return err;
 -        oc = c->avf;
 +        oc = seg->avf;
      }
  
 -    if (c->wrap)
 -        c->number %= c->wrap;
 +    seg->segment_idx++;
 +    if ((seg->segment_idx_wrap) && (seg->segment_idx % seg->segment_idx_wrap == 0))
 +        seg->segment_idx_wrap_nb++;
  
 -    if (av_get_frame_filename(oc->filename, sizeof(oc->filename),
 -                              s->filename, c->number++) < 0)
 -        return AVERROR(EINVAL);
 +    if ((err = set_segment_filename(s)) < 0)
 +        return err;
  
-     if ((err = ffio_open_whitelist(&oc->pb, oc->filename, AVIO_FLAG_WRITE,
-                                    &s->interrupt_callback, NULL, s->protocol_whitelist)) < 0) {
 -    if ((err = s->io_open(s, &oc->pb, oc->filename, AVIO_FLAG_WRITE, NULL)) < 0)
++    if ((err = s->io_open(s, &oc->pb, oc->filename, AVIO_FLAG_WRITE, NULL)) < 0) {
 +        av_log(s, AV_LOG_ERROR, "Failed to open segment '%s'\n", oc->filename);
          return err;
 +    }
 +    if (!seg->individual_header_trailer)
 +        oc->pb->seekable = 0;
  
      if (oc->oformat->priv_class && oc->priv_data)
 -        av_opt_set(oc->priv_data, "resend_headers", "1", 0); /* mpegts specific */
 +        av_opt_set(oc->priv_data, "mpegts_flags", "+resend_headers", 0);
  
      if (write_header) {
          if ((err = avformat_write_header(oc, NULL)) < 0)
      return 0;
  }
  
 -static int segment_end(AVFormatContext *oc, int write_trailer)
 +static int segment_list_open(AVFormatContext *s)
 +{
 +    SegmentContext *seg = s->priv_data;
 +    int ret;
 +
 +    snprintf(seg->temp_list_filename, sizeof(seg->temp_list_filename), seg->use_rename ? "%s.tmp" : "%s", seg->list);
-     ret = ffio_open_whitelist(&seg->list_pb, seg->temp_list_filename, AVIO_FLAG_WRITE,
-                      &s->interrupt_callback, NULL, s->protocol_whitelist);
++    ret = s->io_open(s, &seg->list_pb, seg->temp_list_filename, AVIO_FLAG_WRITE, NULL);
 +    if (ret < 0) {
 +        av_log(s, AV_LOG_ERROR, "Failed to open segment list '%s'\n", seg->list);
 +        return ret;
 +    }
 +
 +    if (seg->list_type == LIST_TYPE_M3U8 && seg->segment_list_entries) {
 +        SegmentListEntry *entry;
 +        double max_duration = 0;
 +
 +        avio_printf(seg->list_pb, "#EXTM3U\n");
 +        avio_printf(seg->list_pb, "#EXT-X-VERSION:3\n");
 +        avio_printf(seg->list_pb, "#EXT-X-MEDIA-SEQUENCE:%d\n", seg->segment_list_entries->index);
 +        avio_printf(seg->list_pb, "#EXT-X-ALLOW-CACHE:%s\n",
 +                    seg->list_flags & SEGMENT_LIST_FLAG_CACHE ? "YES" : "NO");
 +
 +        av_log(s, AV_LOG_VERBOSE, "EXT-X-MEDIA-SEQUENCE:%d\n",
 +               seg->segment_list_entries->index);
 +
 +        for (entry = seg->segment_list_entries; entry; entry = entry->next)
 +            max_duration = FFMAX(max_duration, entry->end_time - entry->start_time);
 +        avio_printf(seg->list_pb, "#EXT-X-TARGETDURATION:%"PRId64"\n", (int64_t)ceil(max_duration));
 +    } else if (seg->list_type == LIST_TYPE_FFCONCAT) {
 +        avio_printf(seg->list_pb, "ffconcat version 1.0\n");
 +    }
 +
 +    return ret;
 +}
 +
 +static void segment_list_print_entry(AVIOContext      *list_ioctx,
 +                                     ListType          list_type,
 +                                     const SegmentListEntry *list_entry,
 +                                     void *log_ctx)
  {
 +    switch (list_type) {
 +    case LIST_TYPE_FLAT:
 +        avio_printf(list_ioctx, "%s\n", list_entry->filename);
 +        break;
 +    case LIST_TYPE_CSV:
 +    case LIST_TYPE_EXT:
 +        print_csv_escaped_str(list_ioctx, list_entry->filename);
 +        avio_printf(list_ioctx, ",%f,%f\n", list_entry->start_time, list_entry->end_time);
 +        break;
 +    case LIST_TYPE_M3U8:
 +        avio_printf(list_ioctx, "#EXTINF:%f,\n%s\n",
 +                    list_entry->end_time - list_entry->start_time, list_entry->filename);
 +        break;
 +    case LIST_TYPE_FFCONCAT:
 +    {
 +        char *buf;
 +        if (av_escape(&buf, list_entry->filename, NULL, AV_ESCAPE_MODE_AUTO, AV_ESCAPE_FLAG_WHITESPACE) < 0) {
 +            av_log(log_ctx, AV_LOG_WARNING,
 +                   "Error writing list entry '%s' in list file\n", list_entry->filename);
 +            return;
 +        }
 +        avio_printf(list_ioctx, "file %s\n", buf);
 +        av_free(buf);
 +        break;
 +    }
 +    default:
 +        av_assert0(!"Invalid list type");
 +    }
 +}
 +
 +static int segment_end(AVFormatContext *s, int write_trailer, int is_last)
 +{
 +    SegmentContext *seg = s->priv_data;
 +    AVFormatContext *oc = seg->avf;
      int ret = 0;
  
      av_write_frame(oc, NULL); /* Flush any buffered data (fragmented mp4) */
      if (write_trailer)
 -        av_write_trailer(oc);
 +        ret = av_write_trailer(oc);
 +
 +    if (ret < 0)
 +        av_log(s, AV_LOG_ERROR, "Failure occurred when ending segment '%s'\n",
 +               oc->filename);
 +
 +    if (seg->list) {
 +        if (seg->list_size || seg->list_type == LIST_TYPE_M3U8) {
 +            SegmentListEntry *entry = av_mallocz(sizeof(*entry));
 +            if (!entry) {
 +                ret = AVERROR(ENOMEM);
 +                goto end;
 +            }
 +
 +            /* append new element */
 +            memcpy(entry, &seg->cur_entry, sizeof(*entry));
 +            entry->filename = av_strdup(entry->filename);
 +            if (!seg->segment_list_entries)
 +                seg->segment_list_entries = seg->segment_list_entries_end = entry;
 +            else
 +                seg->segment_list_entries_end->next = entry;
 +            seg->segment_list_entries_end = entry;
 +
 +            /* drop first item */
 +            if (seg->list_size && seg->segment_count >= seg->list_size) {
 +                entry = seg->segment_list_entries;
 +                seg->segment_list_entries = seg->segment_list_entries->next;
 +                av_freep(&entry->filename);
 +                av_freep(&entry);
 +            }
 +
 +            if ((ret = segment_list_open(s)) < 0)
 +                goto end;
 +            for (entry = seg->segment_list_entries; entry; entry = entry->next)
 +                segment_list_print_entry(seg->list_pb, seg->list_type, entry, s);
 +            if (seg->list_type == LIST_TYPE_M3U8 && is_last)
 +                avio_printf(seg->list_pb, "#EXT-X-ENDLIST\n");
-             avio_closep(&seg->list_pb);
++            ff_format_io_close(s, &seg->list_pb);
 +            if (seg->use_rename)
 +                ff_rename(seg->temp_list_filename, seg->list, s);
 +        } else {
 +            segment_list_print_entry(seg->list_pb, seg->list_type, &seg->cur_entry, s);
 +            avio_flush(seg->list_pb);
 +        }
 +    }
 +
 +    av_log(s, AV_LOG_VERBOSE, "segment:'%s' count:%d ended\n",
 +           seg->avf->filename, seg->segment_count);
 +    seg->segment_count++;
 +
 +end:
-     avio_closep(&oc->pb);
+     ff_format_io_close(oc, &oc->pb);
  
      return ret;
  }
@@@ -591,7 -192,7 +592,7 @@@ static int select_reference_stream(AVFo
  
  static void seg_free_context(SegmentContext *seg)
  {
-     avio_closep(&seg->list_pb);
 -    ff_format_io_close(seg->avf, &seg->pb);
++    ff_format_io_close(seg->avf, &seg->list_pb);
      avformat_free_context(seg->avf);
      seg->avf = NULL;
  }
@@@ -696,59 -239,39 +697,57 @@@ static int seg_write_header(AVFormatCon
          goto fail;
      oc = seg->avf;
  
 -    if (av_get_frame_filename(oc->filename, sizeof(oc->filename),
 -                              s->filename, seg->number++) < 0) {
 -        ret = AVERROR(EINVAL);
 +    if ((ret = set_segment_filename(s)) < 0)
          goto fail;
 -    }
  
      if (seg->write_header_trailer) {
-         if ((ret = ffio_open_whitelist(&oc->pb, seg->header_filename ? seg->header_filename : oc->filename, AVIO_FLAG_WRITE,
-                               &s->interrupt_callback, NULL, s->protocol_whitelist)) < 0) {
 -        if ((ret = s->io_open(s, &oc->pb, oc->filename, AVIO_FLAG_WRITE, NULL)) < 0)
++        if ((ret = s->io_open(s, &oc->pb, oc->filename, AVIO_FLAG_WRITE, NULL)) < 0) {
 +            av_log(s, AV_LOG_ERROR, "Failed to open segment '%s'\n", oc->filename);
              goto fail;
 +        }
 +        if (!seg->individual_header_trailer)
 +            oc->pb->seekable = 0;
      } else {
          if ((ret = open_null_ctx(&oc->pb)) < 0)
              goto fail;
      }
  
 -    if ((ret = avformat_write_header(oc, NULL)) < 0) {
 +    av_dict_copy(&options, seg->format_options, 0);
 +    ret = avformat_write_header(oc, &options);
 +    if (av_dict_count(options)) {
 +        av_log(s, AV_LOG_ERROR,
 +               "Some of the provided format options in '%s' are not recognized\n", seg->format_options_str);
 +        ret = AVERROR(EINVAL);
 +        goto fail;
 +    }
 +
 +    if (ret < 0) {
-         avio_closep(&oc->pb);
+         ff_format_io_close(oc, &oc->pb);
          goto fail;
      }
 +    seg->segment_frame_count = 0;
  
 -    if (!seg->write_header_trailer) {
 -        close_null_ctx(oc->pb);
 -        if ((ret = s->io_open(s, &oc->pb, oc->filename, AVIO_FLAG_WRITE, NULL)) < 0)
 -            goto fail;
 +    av_assert0(s->nb_streams == oc->nb_streams);
 +    for (i = 0; i < s->nb_streams; i++) {
 +        AVStream *inner_st  = oc->streams[i];
 +        AVStream *outer_st = s->streams[i];
 +        avpriv_set_pts_info(outer_st, inner_st->pts_wrap_bits, inner_st->time_base.num, inner_st->time_base.den);
      }
  
 -    if (seg->list) {
 -        if (seg->list_type == LIST_HLS) {
 -            if ((ret = segment_hls_window(s, 0)) < 0)
 -                goto fail;
 +    if (oc->avoid_negative_ts > 0 && s->avoid_negative_ts < 0)
 +        s->avoid_negative_ts = 1;
 +
 +    if (!seg->write_header_trailer || seg->header_filename) {
 +        if (seg->header_filename) {
 +            av_write_frame(oc, NULL);
-             avio_closep(&oc->pb);
++            ff_format_io_close(oc, &oc->pb);
          } else {
 -            avio_printf(seg->pb, "%s\n", oc->filename);
 -            avio_flush(seg->pb);
 +            close_null_ctxp(&oc->pb);
          }
-         if ((ret = ffio_open_whitelist(&oc->pb, oc->filename, AVIO_FLAG_WRITE,
-                               &s->interrupt_callback, NULL, s->protocol_whitelist)) < 0)
++        if ((ret = oc->io_open(oc, &oc->pb, oc->filename, AVIO_FLAG_WRITE, NULL)) < 0)
 +            goto fail;
 +        if (!seg->individual_header_trailer)
 +            oc->pb->seekable = 0;
      }
  
  fail:
@@@ -885,30 -353,22 +884,30 @@@ static int seg_write_trailer(struct AVF
          if ((ret = open_null_ctx(&oc->pb)) < 0)
              goto fail;
          ret = av_write_trailer(oc);
 -        close_null_ctx(oc->pb);
 +        close_null_ctxp(&oc->pb);
      } else {
 -        ret = segment_end(oc, 1);
 +        ret = segment_end(s, 1, 1);
      }
 -
 -    if (ret < 0)
 -        goto fail;
 -
 -    if (seg->list && seg->list_type == LIST_HLS) {
 -        if ((ret = segment_hls_window(s, 1) < 0))
 -            goto fail;
 +fail:
 +    if (seg->list)
-         avio_closep(&seg->list_pb);
++        ff_format_io_close(s, &seg->list_pb);
 +
 +    av_dict_free(&seg->format_options);
 +    av_opt_free(seg);
 +    av_freep(&seg->times);
 +    av_freep(&seg->frames);
 +    av_freep(&seg->cur_entry.filename);
 +
 +    cur = seg->segment_list_entries;
 +    while (cur) {
 +        next = cur->next;
 +        av_freep(&cur->filename);
 +        av_free(cur);
 +        cur = next;
      }
  
 -fail:
 -    ff_format_io_close(s, &seg->pb);
      avformat_free_context(oc);
 +    seg->avf = NULL;
      return ret;
  }
  
@@@ -285,8 -282,8 +285,8 @@@ static int write_manifest(AVFormatConte
      }
      avio_printf(out, "</SmoothStreamingMedia>\n");
      avio_flush(out);
-     avio_close(out);
+     ff_format_io_close(s, &out);
 -    return ff_rename(temp_filename, filename);
 +    return ff_rename(temp_filename, filename, s);
  }
  
  static int ism_write_header(AVFormatContext *s)
index 4993ca3,0000000..1390705
mode 100644,000000..100644
--- /dev/null
@@@ -1,481 -1,0 +1,480 @@@
-         if ((ret = ffio_open_whitelist(&avf2->pb, filename, AVIO_FLAG_WRITE,
-                                        &avf->interrupt_callback, NULL,
-                                        avf->protocol_whitelist)) < 0) {
 +/*
 + * Tee pseudo-muxer
 + * 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/avutil.h"
 +#include "libavutil/avstring.h"
 +#include "libavutil/opt.h"
++#include "internal.h"
 +#include "avformat.h"
 +#include "avio_internal.h"
 +
 +#define MAX_SLAVES 16
 +
 +typedef struct {
 +    AVFormatContext *avf;
 +    AVBitStreamFilterContext **bsfs; ///< bitstream filters per stream
 +
 +    /** map from input to output streams indexes,
 +     * disabled output streams are set to -1 */
 +    int *stream_map;
 +} TeeSlave;
 +
 +typedef struct TeeContext {
 +    const AVClass *class;
 +    unsigned nb_slaves;
 +    TeeSlave slaves[MAX_SLAVES];
 +} TeeContext;
 +
 +static const char *const slave_delim     = "|";
 +static const char *const slave_opt_open  = "[";
 +static const char *const slave_opt_close = "]";
 +static const char *const slave_opt_delim = ":]"; /* must have the close too */
 +static const char *const slave_bsfs_spec_sep = "/";
 +static const char *const slave_select_sep = ",";
 +
 +static const AVClass tee_muxer_class = {
 +    .class_name = "Tee muxer",
 +    .item_name  = av_default_item_name,
 +    .version    = LIBAVUTIL_VERSION_INT,
 +};
 +
 +static int parse_slave_options(void *log, char *slave,
 +                               AVDictionary **options, char **filename)
 +{
 +    const char *p;
 +    char *key, *val;
 +    int ret;
 +
 +    if (!strspn(slave, slave_opt_open)) {
 +        *filename = slave;
 +        return 0;
 +    }
 +    p = slave + 1;
 +    if (strspn(p, slave_opt_close)) {
 +        *filename = (char *)p + 1;
 +        return 0;
 +    }
 +    while (1) {
 +        ret = av_opt_get_key_value(&p, "=", slave_opt_delim, 0, &key, &val);
 +        if (ret < 0) {
 +            av_log(log, AV_LOG_ERROR, "No option found near \"%s\"\n", p);
 +            goto fail;
 +        }
 +        ret = av_dict_set(options, key, val,
 +                          AV_DICT_DONT_STRDUP_KEY | AV_DICT_DONT_STRDUP_VAL);
 +        if (ret < 0)
 +            goto fail;
 +        if (strspn(p, slave_opt_close))
 +            break;
 +        p++;
 +    }
 +    *filename = (char *)p + 1;
 +    return 0;
 +
 +fail:
 +    av_dict_free(options);
 +    return ret;
 +}
 +
 +/**
 + * Parse list of bitstream filters and add them to the list of filters
 + * pointed to by bsfs.
 + *
 + * The list must be specified in the form:
 + * BSFS ::= BSF[,BSFS]
 + */
 +static int parse_bsfs(void *log_ctx, const char *bsfs_spec,
 +                      AVBitStreamFilterContext **bsfs)
 +{
 +    char *bsf_name, *buf, *dup, *saveptr;
 +    int ret = 0;
 +
 +    if (!(dup = buf = av_strdup(bsfs_spec)))
 +        return AVERROR(ENOMEM);
 +
 +    while (bsf_name = av_strtok(buf, ",", &saveptr)) {
 +        AVBitStreamFilterContext *bsf = av_bitstream_filter_init(bsf_name);
 +
 +        if (!bsf) {
 +            av_log(log_ctx, AV_LOG_ERROR,
 +                   "Cannot initialize bitstream filter with name '%s', "
 +                   "unknown filter or internal error happened\n",
 +                   bsf_name);
 +            ret = AVERROR_UNKNOWN;
 +            goto end;
 +        }
 +
 +        /* append bsf context to the list of bsf contexts */
 +        *bsfs = bsf;
 +        bsfs = &bsf->next;
 +
 +        buf = NULL;
 +    }
 +
 +end:
 +    av_free(dup);
 +    return ret;
 +}
 +
 +static int open_slave(AVFormatContext *avf, char *slave, TeeSlave *tee_slave)
 +{
 +    int i, ret;
 +    AVDictionary *options = NULL;
 +    AVDictionaryEntry *entry;
 +    char *filename;
 +    char *format = NULL, *select = NULL;
 +    AVFormatContext *avf2 = NULL;
 +    AVStream *st, *st2;
 +    int stream_count;
 +    int fullret;
 +    char *subselect = NULL, *next_subselect = NULL, *first_subselect = NULL, *tmp_select = NULL;
 +
 +    if ((ret = parse_slave_options(avf, slave, &options, &filename)) < 0)
 +        return ret;
 +
 +#define STEAL_OPTION(option, field) do {                                \
 +        if ((entry = av_dict_get(options, option, NULL, 0))) {          \
 +            field = entry->value;                                       \
 +            entry->value = NULL; /* prevent it from being freed */      \
 +            av_dict_set(&options, option, NULL, 0);                     \
 +        }                                                               \
 +    } while (0)
 +
 +    STEAL_OPTION("f", format);
 +    STEAL_OPTION("select", select);
 +
 +    ret = avformat_alloc_output_context2(&avf2, NULL, format, filename);
 +    if (ret < 0)
 +        goto end;
 +    av_dict_copy(&avf2->metadata, avf->metadata, 0);
++    avf2->opaque   = avf->opaque;
++    avf2->io_open  = avf->io_open;
++    avf2->io_close = avf->io_close;
 +
 +    tee_slave->stream_map = av_calloc(avf->nb_streams, sizeof(*tee_slave->stream_map));
 +    if (!tee_slave->stream_map) {
 +        ret = AVERROR(ENOMEM);
 +        goto end;
 +    }
 +
 +    stream_count = 0;
 +    for (i = 0; i < avf->nb_streams; i++) {
 +        st = avf->streams[i];
 +        if (select) {
 +            tmp_select = av_strdup(select);  // av_strtok is destructive so we regenerate it in each loop
 +            if (!tmp_select) {
 +                ret = AVERROR(ENOMEM);
 +                goto end;
 +            }
 +            fullret = 0;
 +            first_subselect = tmp_select;
 +            next_subselect = NULL;
 +            while (subselect = av_strtok(first_subselect, slave_select_sep, &next_subselect)) {
 +                first_subselect = NULL;
 +
 +                ret = avformat_match_stream_specifier(avf, avf->streams[i], subselect);
 +                if (ret < 0) {
 +                    av_log(avf, AV_LOG_ERROR,
 +                           "Invalid stream specifier '%s' for output '%s'\n",
 +                           subselect, slave);
 +                    goto end;
 +                }
 +                if (ret != 0) {
 +                    fullret = 1; // match
 +                    break;
 +                }
 +            }
 +            av_freep(&tmp_select);
 +
 +            if (fullret == 0) { /* no match */
 +                tee_slave->stream_map[i] = -1;
 +                continue;
 +            }
 +        }
 +        tee_slave->stream_map[i] = stream_count++;
 +
 +        if (!(st2 = avformat_new_stream(avf2, NULL))) {
 +            ret = AVERROR(ENOMEM);
 +            goto end;
 +        }
 +        st2->id = st->id;
 +        st2->r_frame_rate        = st->r_frame_rate;
 +        st2->time_base           = st->time_base;
 +        st2->start_time          = st->start_time;
 +        st2->duration            = st->duration;
 +        st2->nb_frames           = st->nb_frames;
 +        st2->disposition         = st->disposition;
 +        st2->sample_aspect_ratio = st->sample_aspect_ratio;
 +        st2->avg_frame_rate      = st->avg_frame_rate;
 +        av_dict_copy(&st2->metadata, st->metadata, 0);
 +        if ((ret = avcodec_copy_context(st2->codec, st->codec)) < 0)
 +            goto end;
 +    }
 +
 +    if (!(avf2->oformat->flags & AVFMT_NOFILE)) {
-         avio_closep(&avf2->pb);
++        if ((ret = avf2->io_open(avf2, &avf2->pb, filename, AVIO_FLAG_WRITE, NULL)) < 0) {
 +            av_log(avf, AV_LOG_ERROR, "Slave '%s': error opening: %s\n",
 +                   slave, av_err2str(ret));
 +            goto end;
 +        }
 +    }
 +
 +    if ((ret = avformat_write_header(avf2, &options)) < 0) {
 +        av_log(avf, AV_LOG_ERROR, "Slave '%s': error writing header: %s\n",
 +               slave, av_err2str(ret));
 +        goto end;
 +    }
 +
 +    tee_slave->avf = avf2;
 +    tee_slave->bsfs = av_calloc(avf2->nb_streams, sizeof(TeeSlave));
 +    if (!tee_slave->bsfs) {
 +        ret = AVERROR(ENOMEM);
 +        goto end;
 +    }
 +
 +    entry = NULL;
 +    while (entry = av_dict_get(options, "bsfs", NULL, AV_DICT_IGNORE_SUFFIX)) {
 +        const char *spec = entry->key + strlen("bsfs");
 +        if (*spec) {
 +            if (strspn(spec, slave_bsfs_spec_sep) != 1) {
 +                av_log(avf, AV_LOG_ERROR,
 +                       "Specifier separator in '%s' is '%c', but only characters '%s' "
 +                       "are allowed\n", entry->key, *spec, slave_bsfs_spec_sep);
 +                return AVERROR(EINVAL);
 +            }
 +            spec++; /* consume separator */
 +        }
 +
 +        for (i = 0; i < avf2->nb_streams; i++) {
 +            ret = avformat_match_stream_specifier(avf2, avf2->streams[i], spec);
 +            if (ret < 0) {
 +                av_log(avf, AV_LOG_ERROR,
 +                       "Invalid stream specifier '%s' in bsfs option '%s' for slave "
 +                       "output '%s'\n", spec, entry->key, filename);
 +                goto end;
 +            }
 +
 +            if (ret > 0) {
 +                av_log(avf, AV_LOG_DEBUG, "spec:%s bsfs:%s matches stream %d of slave "
 +                       "output '%s'\n", spec, entry->value, i, filename);
 +                if (tee_slave->bsfs[i]) {
 +                    av_log(avf, AV_LOG_WARNING,
 +                           "Duplicate bsfs specification associated to stream %d of slave "
 +                           "output '%s', filters will be ignored\n", i, filename);
 +                    continue;
 +                }
 +                ret = parse_bsfs(avf, entry->value, &tee_slave->bsfs[i]);
 +                if (ret < 0) {
 +                    av_log(avf, AV_LOG_ERROR,
 +                           "Error parsing bitstream filter sequence '%s' associated to "
 +                           "stream %d of slave output '%s'\n", entry->value, i, filename);
 +                    goto end;
 +                }
 +            }
 +        }
 +
 +        av_dict_set(&options, entry->key, NULL, 0);
 +    }
 +
 +    if (options) {
 +        entry = NULL;
 +        while ((entry = av_dict_get(options, "", entry, AV_DICT_IGNORE_SUFFIX)))
 +            av_log(avf2, AV_LOG_ERROR, "Unknown option '%s'\n", entry->key);
 +        ret = AVERROR_OPTION_NOT_FOUND;
 +        goto end;
 +    }
 +
 +end:
 +    av_free(format);
 +    av_free(select);
 +    av_dict_free(&options);
 +    av_freep(&tmp_select);
 +    return ret;
 +}
 +
 +static void close_slaves(AVFormatContext *avf)
 +{
 +    TeeContext *tee = avf->priv_data;
 +    AVFormatContext *avf2;
 +    unsigned i, j;
 +
 +    for (i = 0; i < tee->nb_slaves; i++) {
 +        avf2 = tee->slaves[i].avf;
 +
 +        for (j = 0; j < avf2->nb_streams; j++) {
 +            AVBitStreamFilterContext *bsf_next, *bsf = tee->slaves[i].bsfs[j];
 +            while (bsf) {
 +                bsf_next = bsf->next;
 +                av_bitstream_filter_close(bsf);
 +                bsf = bsf_next;
 +            }
 +        }
 +        av_freep(&tee->slaves[i].stream_map);
 +        av_freep(&tee->slaves[i].bsfs);
 +
-         if (!(avf2->oformat->flags & AVFMT_NOFILE)) {
-             if ((ret = avio_closep(&avf2->pb)) < 0)
-                 if (!ret_all)
-                     ret_all = ret;
-         }
++        ff_format_io_close(avf2, &avf2->pb);
 +        avformat_free_context(avf2);
 +        tee->slaves[i].avf = NULL;
 +    }
 +}
 +
 +static void log_slave(TeeSlave *slave, void *log_ctx, int log_level)
 +{
 +    int i;
 +    av_log(log_ctx, log_level, "filename:'%s' format:%s\n",
 +           slave->avf->filename, slave->avf->oformat->name);
 +    for (i = 0; i < slave->avf->nb_streams; i++) {
 +        AVStream *st = slave->avf->streams[i];
 +        AVBitStreamFilterContext *bsf = slave->bsfs[i];
 +
 +        av_log(log_ctx, log_level, "    stream:%d codec:%s type:%s",
 +               i, avcodec_get_name(st->codec->codec_id),
 +               av_get_media_type_string(st->codec->codec_type));
 +        if (bsf) {
 +            av_log(log_ctx, log_level, " bsfs:");
 +            while (bsf) {
 +                av_log(log_ctx, log_level, "%s%s",
 +                       bsf->filter->name, bsf->next ? "," : "");
 +                bsf = bsf->next;
 +            }
 +        }
 +        av_log(log_ctx, log_level, "\n");
 +    }
 +}
 +
 +static int tee_write_header(AVFormatContext *avf)
 +{
 +    TeeContext *tee = avf->priv_data;
 +    unsigned nb_slaves = 0, i;
 +    const char *filename = avf->filename;
 +    char *slaves[MAX_SLAVES];
 +    int ret;
 +
 +    while (*filename) {
 +        if (nb_slaves == MAX_SLAVES) {
 +            av_log(avf, AV_LOG_ERROR, "Maximum %d slave muxers reached.\n",
 +                   MAX_SLAVES);
 +            ret = AVERROR_PATCHWELCOME;
 +            goto fail;
 +        }
 +        if (!(slaves[nb_slaves++] = av_get_token(&filename, slave_delim))) {
 +            ret = AVERROR(ENOMEM);
 +            goto fail;
 +        }
 +        if (strspn(filename, slave_delim))
 +            filename++;
 +    }
 +
 +    for (i = 0; i < nb_slaves; i++) {
 +        if ((ret = open_slave(avf, slaves[i], &tee->slaves[i])) < 0)
 +            goto fail;
 +        log_slave(&tee->slaves[i], avf, AV_LOG_VERBOSE);
 +        av_freep(&slaves[i]);
 +    }
 +
 +    tee->nb_slaves = nb_slaves;
 +
 +    for (i = 0; i < avf->nb_streams; i++) {
 +        int j, mapped = 0;
 +        for (j = 0; j < tee->nb_slaves; j++)
 +            mapped += tee->slaves[j].stream_map[i] >= 0;
 +        if (!mapped)
 +            av_log(avf, AV_LOG_WARNING, "Input stream #%d is not mapped "
 +                   "to any slave.\n", i);
 +    }
 +    return 0;
 +
 +fail:
 +    for (i = 0; i < nb_slaves; i++)
 +        av_freep(&slaves[i]);
 +    close_slaves(avf);
 +    return ret;
 +}
 +
 +static int tee_write_trailer(AVFormatContext *avf)
 +{
 +    TeeContext *tee = avf->priv_data;
 +    AVFormatContext *avf2;
 +    int ret_all = 0, ret;
 +    unsigned i;
 +
 +    for (i = 0; i < tee->nb_slaves; i++) {
 +        avf2 = tee->slaves[i].avf;
 +        if ((ret = av_write_trailer(avf2)) < 0)
 +            if (!ret_all)
 +                ret_all = ret;
++        if (!(avf2->oformat->flags & AVFMT_NOFILE))
++            ff_format_io_close(avf2, &avf2->pb);
 +    }
 +    close_slaves(avf);
 +    return ret_all;
 +}
 +
 +static int tee_write_packet(AVFormatContext *avf, AVPacket *pkt)
 +{
 +    TeeContext *tee = avf->priv_data;
 +    AVFormatContext *avf2;
 +    AVPacket pkt2;
 +    int ret_all = 0, ret;
 +    unsigned i, s;
 +    int s2;
 +    AVRational tb, tb2;
 +
 +    for (i = 0; i < tee->nb_slaves; i++) {
 +        avf2 = tee->slaves[i].avf;
 +        s = pkt->stream_index;
 +        s2 = tee->slaves[i].stream_map[s];
 +        if (s2 < 0)
 +            continue;
 +
 +        if ((ret = av_copy_packet(&pkt2, pkt)) < 0 ||
 +            (ret = av_dup_packet(&pkt2))< 0)
 +            if (!ret_all) {
 +                ret_all = ret;
 +                continue;
 +            }
 +        tb  = avf ->streams[s ]->time_base;
 +        tb2 = avf2->streams[s2]->time_base;
 +        pkt2.pts      = av_rescale_q(pkt->pts,      tb, tb2);
 +        pkt2.dts      = av_rescale_q(pkt->dts,      tb, tb2);
 +        pkt2.duration = av_rescale_q(pkt->duration, tb, tb2);
 +        pkt2.stream_index = s2;
 +
 +        if ((ret = av_apply_bitstream_filters(avf2->streams[s2]->codec, &pkt2,
 +                                              tee->slaves[i].bsfs[s2])) < 0 ||
 +            (ret = av_interleaved_write_frame(avf2, &pkt2)) < 0)
 +            if (!ret_all)
 +                ret_all = ret;
 +    }
 +    return ret_all;
 +}
 +
 +AVOutputFormat ff_tee_muxer = {
 +    .name              = "tee",
 +    .long_name         = NULL_IF_CONFIG_SMALL("Multiple muxer tee"),
 +    .priv_data_size    = sizeof(TeeContext),
 +    .write_header      = tee_write_header,
 +    .write_trailer     = tee_write_trailer,
 +    .write_packet      = tee_write_packet,
 +    .priv_class        = &tee_muxer_class,
 +    .flags             = AVFMT_NOFILE,
 +};
@@@ -73,110 -68,7 +73,114 @@@ const char *avformat_configuration(void
  const char *avformat_license(void)
  {
  #define LICENSE_PREFIX "libavformat license: "
 -    return LICENSE_PREFIX LIBAV_LICENSE + sizeof(LICENSE_PREFIX) - 1;
 +    return LICENSE_PREFIX FFMPEG_LICENSE + sizeof(LICENSE_PREFIX) - 1;
 +}
 +
 +#define RELATIVE_TS_BASE (INT64_MAX - (1LL<<48))
 +
 +static int is_relative(int64_t ts) {
 +    return ts > (RELATIVE_TS_BASE - (1LL<<48));
 +}
 +
 +/**
 + * Wrap a given time stamp, if there is an indication for an overflow
 + *
 + * @param st stream
 + * @param timestamp the time stamp to wrap
 + * @return resulting time stamp
 + */
 +static int64_t wrap_timestamp(AVStream *st, int64_t timestamp)
 +{
 +    if (st->pts_wrap_behavior != AV_PTS_WRAP_IGNORE &&
 +        st->pts_wrap_reference != AV_NOPTS_VALUE && timestamp != AV_NOPTS_VALUE) {
 +        if (st->pts_wrap_behavior == AV_PTS_WRAP_ADD_OFFSET &&
 +            timestamp < st->pts_wrap_reference)
 +            return timestamp + (1ULL << st->pts_wrap_bits);
 +        else if (st->pts_wrap_behavior == AV_PTS_WRAP_SUB_OFFSET &&
 +            timestamp >= st->pts_wrap_reference)
 +            return timestamp - (1ULL << st->pts_wrap_bits);
 +    }
 +    return timestamp;
 +}
 +
 +MAKE_ACCESSORS(AVStream, stream, AVRational, r_frame_rate)
 +MAKE_ACCESSORS(AVStream, stream, char *, recommended_encoder_configuration)
 +MAKE_ACCESSORS(AVFormatContext, format, AVCodec *, video_codec)
 +MAKE_ACCESSORS(AVFormatContext, format, AVCodec *, audio_codec)
 +MAKE_ACCESSORS(AVFormatContext, format, AVCodec *, subtitle_codec)
 +MAKE_ACCESSORS(AVFormatContext, format, AVCodec *, data_codec)
 +MAKE_ACCESSORS(AVFormatContext, format, int, metadata_header_padding)
 +MAKE_ACCESSORS(AVFormatContext, format, void *, opaque)
 +MAKE_ACCESSORS(AVFormatContext, format, av_format_control_message, control_message_cb)
++#if FF_API_OLD_OPEN_CALLBACKS
++FF_DISABLE_DEPRECATION_WARNINGS
 +MAKE_ACCESSORS(AVFormatContext, format, AVOpenCallback, open_cb)
++FF_ENABLE_DEPRECATION_WARNINGS
++#endif
 +
 +int64_t av_stream_get_end_pts(const AVStream *st)
 +{
 +    if (st->priv_pts) {
 +        return st->priv_pts->val;
 +    } else
 +        return AV_NOPTS_VALUE;
 +}
 +
 +struct AVCodecParserContext *av_stream_get_parser(const AVStream *st)
 +{
 +    return st->parser;
 +}
 +
 +void av_format_inject_global_side_data(AVFormatContext *s)
 +{
 +    int i;
 +    s->internal->inject_global_side_data = 1;
 +    for (i = 0; i < s->nb_streams; i++) {
 +        AVStream *st = s->streams[i];
 +        st->inject_global_side_data = 1;
 +    }
 +}
 +
 +int ff_copy_whitelists(AVFormatContext *dst, AVFormatContext *src)
 +{
 +    av_assert0(!dst->codec_whitelist &&
 +               !dst->format_whitelist &&
 +               !dst->protocol_whitelist);
 +    dst-> codec_whitelist = av_strdup(src->codec_whitelist);
 +    dst->format_whitelist = av_strdup(src->format_whitelist);
 +    dst->protocol_whitelist = av_strdup(src->protocol_whitelist);
 +    if (   (src-> codec_whitelist && !dst-> codec_whitelist)
 +        || (src->  format_whitelist && !dst->  format_whitelist)
 +        || (src->protocol_whitelist && !dst->protocol_whitelist)) {
 +        av_log(dst, AV_LOG_ERROR, "Failed to duplicate whitelist\n");
 +        return AVERROR(ENOMEM);
 +    }
 +    return 0;
 +}
 +
 +static const AVCodec *find_decoder(AVFormatContext *s, AVStream *st, enum AVCodecID codec_id)
 +{
 +    if (st->codec->codec)
 +        return st->codec->codec;
 +
 +    switch (st->codec->codec_type) {
 +    case AVMEDIA_TYPE_VIDEO:
 +        if (s->video_codec)    return s->video_codec;
 +        break;
 +    case AVMEDIA_TYPE_AUDIO:
 +        if (s->audio_codec)    return s->audio_codec;
 +        break;
 +    case AVMEDIA_TYPE_SUBTITLE:
 +        if (s->subtitle_codec) return s->subtitle_codec;
 +        break;
 +    }
 +
 +    return avcodec_find_decoder(codec_id);
 +}
 +
 +int av_format_get_probe_score(const AVFormatContext *s)
 +{
 +    return s->probe_score;
  }
  
  /* an arbitrarily chosen "sane" max packet size -- 50M */
@@@ -353,18 -197,16 +357,16 @@@ static int init_input(AVFormatContext *
      }
  
      if ((s->iformat && s->iformat->flags & AVFMT_NOFILE) ||
 -        (!s->iformat && (s->iformat = av_probe_input_format(&pd, 0))))
 -        return 0;
 +        (!s->iformat && (s->iformat = av_probe_input_format2(&pd, 0, &score))))
 +        return score;
  
-     if ((ret = ffio_open_whitelist(&s->pb, filename, AVIO_FLAG_READ | s->avio_flags,
-                                    &s->interrupt_callback, options,
-                                    s->protocol_whitelist)) < 0)
 -    if ((ret = avio_open2(&s->pb, filename, AVIO_FLAG_READ,
 -                          &s->interrupt_callback, options)) < 0)
++    if ((ret = s->io_open(s, &s->pb, filename, AVIO_FLAG_READ | s->avio_flags, options)) < 0)
          return ret;
 +
      if (s->iformat)
          return 0;
 -    return av_probe_input_buffer(s->pb, &s->iformat, filename,
 -                                 s, 0, s->probesize);
 +    return av_probe_input_buffer2(s->pb, &s->iformat, filename,
 +                                 s, 0, s->format_probesize);
  }
  
  static int add_to_pktbuf(AVPacketList **packet_buffer, AVPacket *pkt,
@@@ -4672,73 -3177,9 +4674,80 @@@ uint8_t *av_stream_new_side_data(AVStre
      return data;
  }
  
 +int ff_stream_add_bitstream_filter(AVStream *st, const char *name, const char *args)
 +{
 +    AVBitStreamFilterContext *bsfc = NULL;
 +    AVBitStreamFilterContext **dest = &st->internal->bsfc;
 +    while (*dest && (*dest)->next)
 +        dest = &(*dest)->next;
 +
 +    if (!(bsfc = av_bitstream_filter_init(name))) {
 +        av_log(NULL, AV_LOG_ERROR, "Unknown bitstream filter '%s'\n", name);
 +        return AVERROR(EINVAL);
 +    }
 +    if (args && !(bsfc->args = av_strdup(args))) {
 +        av_bitstream_filter_close(bsfc);
 +        return AVERROR(ENOMEM);
 +    }
 +    av_log(st->codec, AV_LOG_VERBOSE,
 +           "Automatically inserted bitstream filter '%s'; args='%s'\n",
 +           name, args ? args : "");
 +    *dest = bsfc;
 +    return 1;
 +}
 +
 +int av_apply_bitstream_filters(AVCodecContext *codec, AVPacket *pkt,
 +                               AVBitStreamFilterContext *bsfc)
 +{
 +    int ret = 0;
 +    while (bsfc) {
 +        AVPacket new_pkt = *pkt;
 +        int a = av_bitstream_filter_filter(bsfc, codec, NULL,
 +                                           &new_pkt.data, &new_pkt.size,
 +                                           pkt->data, pkt->size,
 +                                           pkt->flags & AV_PKT_FLAG_KEY);
 +        if(a == 0 && new_pkt.data != pkt->data) {
 +            uint8_t *t = av_malloc(new_pkt.size + AV_INPUT_BUFFER_PADDING_SIZE); //the new should be a subset of the old so cannot overflow
 +            if (t) {
 +                memcpy(t, new_pkt.data, new_pkt.size);
 +                memset(t + new_pkt.size, 0, AV_INPUT_BUFFER_PADDING_SIZE);
 +                new_pkt.data = t;
 +                new_pkt.buf = NULL;
 +                a = 1;
 +            } else {
 +                a = AVERROR(ENOMEM);
 +            }
 +        }
 +        if (a > 0) {
 +            new_pkt.buf = av_buffer_create(new_pkt.data, new_pkt.size,
 +                                           av_buffer_default_free, NULL, 0);
 +            if (new_pkt.buf) {
 +                pkt->side_data = NULL;
 +                pkt->side_data_elems = 0;
 +                av_packet_unref(pkt);
 +            } else {
 +                av_freep(&new_pkt.data);
 +                a = AVERROR(ENOMEM);
 +            }
 +        }
 +        if (a < 0) {
 +            av_log(codec, AV_LOG_ERROR,
 +                   "Failed to open bitstream filter %s for stream %d with codec %s",
 +                   bsfc->filter->name, pkt->stream_index,
 +                   codec->codec ? codec->codec->name : "copy");
 +            ret = a;
 +            break;
 +        }
 +        *pkt = new_pkt;
 +
 +        bsfc = bsfc->next;
 +    }
 +    return ret;
 +}
++
+ void ff_format_io_close(AVFormatContext *s, AVIOContext **pb)
+ {
+     if (*pb)
+         s->io_close(s, *pb);
+     *pb = NULL;
+ }
@@@ -29,9 -29,9 +29,9 @@@
  
  #include "libavutil/version.h"
  
 -#define LIBAVFORMAT_VERSION_MAJOR 57
 -#define LIBAVFORMAT_VERSION_MINOR  3
 -#define LIBAVFORMAT_VERSION_MICRO  0
 +#define LIBAVFORMAT_VERSION_MAJOR  57
- #define LIBAVFORMAT_VERSION_MINOR  24
- #define LIBAVFORMAT_VERSION_MICRO 101
++#define LIBAVFORMAT_VERSION_MINOR  25
++#define LIBAVFORMAT_VERSION_MICRO 100
  
  #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \
                                                 LIBAVFORMAT_VERSION_MINOR, \
  #ifndef FF_API_COMPUTE_PKT_FIELDS2
  #define FF_API_COMPUTE_PKT_FIELDS2      (LIBAVFORMAT_VERSION_MAJOR < 58)
  #endif
++#ifndef FF_API_OLD_OPEN_CALLBACKS
++#define FF_API_OLD_OPEN_CALLBACKS       (LIBAVFORMAT_VERSION_MAJOR < 58)
++#endif
  
 +#ifndef FF_API_R_FRAME_RATE
 +#define FF_API_R_FRAME_RATE            1
 +#endif
  #endif /* AVFORMAT_VERSION_H */
index 0ae6c3a,0000000..063eb3b
mode 100644,000000..100644
--- /dev/null
@@@ -1,268 -1,0 +1,264 @@@
-     ret = ffio_open_whitelist(&oc->pb, oc->filename, AVIO_FLAG_WRITE,
-                               &s->interrupt_callback, NULL, s->protocol_whitelist);
 +/*
 + * Copyright (c) 2015, Vignesh Venkatasubramanian
 + *
 + * 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
 + */
 +
 +/**
 + * @file WebM Chunk Muxer
 + * The chunk muxer enables writing WebM Live chunks where there is a header
 + * chunk, followed by data chunks where each Cluster is written out as a Chunk.
 + */
 +
 +#include <float.h>
 +#include <time.h>
 +
 +#include "avformat.h"
 +#include "avio.h"
 +#include "avio_internal.h"
 +#include "internal.h"
 +
 +#include "libavutil/avassert.h"
 +#include "libavutil/log.h"
 +#include "libavutil/opt.h"
 +#include "libavutil/avstring.h"
 +#include "libavutil/parseutils.h"
 +#include "libavutil/mathematics.h"
 +#include "libavutil/time.h"
 +#include "libavutil/time_internal.h"
 +#include "libavutil/timestamp.h"
 +
 +#define MAX_FILENAME_SIZE 1024
 +
 +typedef struct WebMChunkContext {
 +    const AVClass *class;
 +    int chunk_start_index;
 +    char *header_filename;
 +    int chunk_duration;
 +    int chunk_index;
 +    uint64_t duration_written;
 +    int prev_pts;
 +    AVOutputFormat *oformat;
 +    AVFormatContext *avf;
 +} WebMChunkContext;
 +
 +static int chunk_mux_init(AVFormatContext *s)
 +{
 +    WebMChunkContext *wc = s->priv_data;
 +    AVFormatContext *oc;
 +    int ret;
 +
 +    ret = avformat_alloc_output_context2(&wc->avf, wc->oformat, NULL, NULL);
 +    if (ret < 0)
 +        return ret;
 +    oc = wc->avf;
 +
 +    oc->interrupt_callback = s->interrupt_callback;
 +    oc->max_delay          = s->max_delay;
 +    av_dict_copy(&oc->metadata, s->metadata, 0);
 +
 +    *(const AVClass**)oc->priv_data = oc->oformat->priv_class;
 +    av_opt_set_defaults(oc->priv_data);
 +    av_opt_set_int(oc->priv_data, "dash", 1, 0);
 +    av_opt_set_int(oc->priv_data, "cluster_time_limit", wc->chunk_duration, 0);
 +    av_opt_set_int(oc->priv_data, "live", 1, 0);
 +
 +    oc->streams = s->streams;
 +    oc->nb_streams = s->nb_streams;
 +
 +    return 0;
 +}
 +
 +static int get_chunk_filename(AVFormatContext *s, int is_header, char *filename)
 +{
 +    WebMChunkContext *wc = s->priv_data;
 +    AVFormatContext *oc = wc->avf;
 +    if (!filename) {
 +        return AVERROR(EINVAL);
 +    }
 +    if (is_header) {
 +        if (!wc->header_filename) {
 +            return AVERROR(EINVAL);
 +        }
 +        av_strlcpy(filename, wc->header_filename, strlen(wc->header_filename) + 1);
 +    } else {
 +        if (av_get_frame_filename(filename, MAX_FILENAME_SIZE,
 +                                  s->filename, wc->chunk_index - 1) < 0) {
 +            av_log(oc, AV_LOG_ERROR, "Invalid chunk filename template '%s'\n", s->filename);
 +            return AVERROR(EINVAL);
 +        }
 +    }
 +    return 0;
 +}
 +
 +static int webm_chunk_write_header(AVFormatContext *s)
 +{
 +    WebMChunkContext *wc = s->priv_data;
 +    AVFormatContext *oc = NULL;
 +    int ret;
 +
 +    // DASH Streams can only have either one track per file.
 +    if (s->nb_streams != 1) { return AVERROR_INVALIDDATA; }
 +
 +    wc->chunk_index = wc->chunk_start_index;
 +    wc->oformat = av_guess_format("webm", s->filename, "video/webm");
 +    if (!wc->oformat)
 +        return AVERROR_MUXER_NOT_FOUND;
 +
 +    ret = chunk_mux_init(s);
 +    if (ret < 0)
 +        return ret;
 +    oc = wc->avf;
 +    ret = get_chunk_filename(s, 1, oc->filename);
 +    if (ret < 0)
 +        return ret;
-     avio_close(oc->pb);
++    ret = s->io_open(s, &oc->pb, oc->filename, AVIO_FLAG_WRITE, NULL);
 +    if (ret < 0)
 +        return ret;
 +
 +    oc->pb->seekable = 0;
 +    ret = oc->oformat->write_header(oc);
 +    if (ret < 0)
 +        return ret;
-     ret = ffio_open_whitelist(&pb, filename, AVIO_FLAG_WRITE,
-                               &s->interrupt_callback, NULL, s->protocol_whitelist);
++    ff_format_io_close(s, &oc->pb);
 +    return 0;
 +}
 +
 +static int chunk_start(AVFormatContext *s)
 +{
 +    WebMChunkContext *wc = s->priv_data;
 +    AVFormatContext *oc = wc->avf;
 +    int ret;
 +
 +    ret = avio_open_dyn_buf(&oc->pb);
 +    if (ret < 0)
 +        return ret;
 +    wc->chunk_index++;
 +    return 0;
 +}
 +
 +static int chunk_end(AVFormatContext *s)
 +{
 +    WebMChunkContext *wc = s->priv_data;
 +    AVFormatContext *oc = wc->avf;
 +    int ret;
 +    int buffer_size;
 +    uint8_t *buffer;
 +    AVIOContext *pb;
 +    char filename[MAX_FILENAME_SIZE];
 +
 +    if (wc->chunk_start_index == wc->chunk_index)
 +        return 0;
 +    // Flush the cluster in WebM muxer.
 +    oc->oformat->write_packet(oc, NULL);
 +    buffer_size = avio_close_dyn_buf(oc->pb, &buffer);
 +    ret = get_chunk_filename(s, 0, filename);
 +    if (ret < 0)
 +        goto fail;
-     ret = avio_close(pb);
-     if (ret < 0)
-         goto fail;
++    ret = s->io_open(s, &pb, filename, AVIO_FLAG_WRITE, NULL);
 +    if (ret < 0)
 +        goto fail;
 +    avio_write(pb, buffer, buffer_size);
++    ff_format_io_close(s, &pb);
 +    oc->pb = NULL;
 +fail:
 +    av_free(buffer);
 +    return (ret < 0) ? ret : 0;
 +}
 +
 +static int webm_chunk_write_packet(AVFormatContext *s, AVPacket *pkt)
 +{
 +    WebMChunkContext *wc = s->priv_data;
 +    AVFormatContext *oc = wc->avf;
 +    AVStream *st = s->streams[pkt->stream_index];
 +    int ret;
 +
 +    if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO) {
 +        wc->duration_written += av_rescale_q(pkt->pts - wc->prev_pts,
 +                                             st->time_base,
 +                                             (AVRational) {1, 1000});
 +        wc->prev_pts = pkt->pts;
 +    }
 +
 +    // For video, a new chunk is started only on key frames. For audio, a new
 +    // chunk is started based on chunk_duration.
 +    if ((st->codec->codec_type == AVMEDIA_TYPE_VIDEO &&
 +         (pkt->flags & AV_PKT_FLAG_KEY)) ||
 +        (st->codec->codec_type == AVMEDIA_TYPE_AUDIO &&
 +         (pkt->pts == 0 || wc->duration_written >= wc->chunk_duration))) {
 +        wc->duration_written = 0;
 +        if ((ret = chunk_end(s)) < 0 || (ret = chunk_start(s)) < 0) {
 +            goto fail;
 +        }
 +    }
 +
 +    ret = oc->oformat->write_packet(oc, pkt);
 +    if (ret < 0)
 +        goto fail;
 +
 +fail:
 +    if (ret < 0) {
 +        oc->streams = NULL;
 +        oc->nb_streams = 0;
 +        avformat_free_context(oc);
 +    }
 +
 +    return ret;
 +}
 +
 +static int webm_chunk_write_trailer(AVFormatContext *s)
 +{
 +    WebMChunkContext *wc = s->priv_data;
 +    AVFormatContext *oc = wc->avf;
 +    oc->oformat->write_trailer(oc);
 +    chunk_end(s);
 +    oc->streams = NULL;
 +    oc->nb_streams = 0;
 +    avformat_free_context(oc);
 +    return 0;
 +}
 +
 +#define OFFSET(x) offsetof(WebMChunkContext, x)
 +static const AVOption options[] = {
 +    { "chunk_start_index",  "start index of the chunk", OFFSET(chunk_start_index), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM },
 +    { "header", "filename of the header where the initialization data will be written", OFFSET(header_filename), AV_OPT_TYPE_STRING, { 0 }, 0, 0, AV_OPT_FLAG_ENCODING_PARAM },
 +    { "audio_chunk_duration", "duration of each chunk in milliseconds", OFFSET(chunk_duration), AV_OPT_TYPE_INT, {.i64 = 5000}, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM },
 +    { NULL },
 +};
 +
 +#if CONFIG_WEBM_CHUNK_MUXER
 +static const AVClass webm_chunk_class = {
 +    .class_name = "WebM Chunk Muxer",
 +    .item_name  = av_default_item_name,
 +    .option     = options,
 +    .version    = LIBAVUTIL_VERSION_INT,
 +};
 +
 +AVOutputFormat ff_webm_chunk_muxer = {
 +    .name           = "webm_chunk",
 +    .long_name      = NULL_IF_CONFIG_SMALL("WebM Chunk Muxer"),
 +    .mime_type      = "video/webm",
 +    .extensions     = "chk",
 +    .flags          = AVFMT_NOFILE | AVFMT_GLOBALHEADER | AVFMT_NEEDNUMBER |
 +                      AVFMT_TS_NONSTRICT,
 +    .priv_data_size = sizeof(WebMChunkContext),
 +    .write_header   = webm_chunk_write_header,
 +    .write_packet   = webm_chunk_write_packet,
 +    .write_trailer  = webm_chunk_write_trailer,
 +    .priv_class     = &webm_chunk_class,
 +};
 +#endif