Merge remote-tracking branch 'qatar/master'
authorMichael Niedermayer <michaelni@gmx.at>
Sat, 2 Nov 2013 09:37:03 +0000 (10:37 +0100)
committerMichael Niedermayer <michaelni@gmx.at>
Sat, 2 Nov 2013 09:38:03 +0000 (10:38 +0100)
* qatar/master:
  Add an HDS live fragmenting muxer

Conflicts:
Changelog
libavformat/version.h

Merged-by: Michael Niedermayer <michaelni@gmx.at>
1  2 
Changelog
configure
libavformat/Makefile
libavformat/allformats.c
libavformat/hdsenc.c
libavformat/version.h

diff --cc Changelog
+++ b/Changelog
  Entries are sorted chronologically from oldest to youngest within each release,
  releases are sorted from youngest to oldest.
  
 -version 10:
 -- av_strnstr
 -- support ID3v2 tags in ASF files
 +version <next>
 +
 +- HNM version 4 demuxer and video decoder
++- Live HDS muxer
 +
 +
 +version 2.1:
 +
 +- aecho filter
 +- perspective filter ported from libmpcodecs
 +- ffprobe -show_programs option
 +- compand filter
 +- RTMP seek support
 +- when transcoding with ffmpeg (i.e. not streamcopying), -ss is now accurate
 +  even when used as an input option. Previous behavior can be restored with
 +  the -noaccurate_seek option.
 +- ffmpeg -t option can now be used for inputs, to limit the duration of
 +  data read from an input file
 +- incomplete Voxware MetaSound decoder
 +- read EXIF metadata from JPEG
 +- DVB teletext decoder
 +- phase filter ported from libmpcodecs
 +- w3fdif filter
 +- Opus support in Matroska
 +- FFV1 version 1.3 is stable and no longer experimental
 +- FFV1: YUVA(444,422,420) 9, 10 and 16 bit support
 +- changed DTS stream id in lavf mpeg ps muxer from 0x8a to 0x88, to be
 +  more consistent with other muxers.
 +- adelay filter
 +- pullup filter ported from libmpcodecs
 +- ffprobe -read_intervals option
 +- Lossless and alpha support for WebP decoder
 +- Error Resilient AAC syntax (ER AAC LC) decoding
 +- Low Delay AAC (ER AAC LD) decoding
 +- mux chapters in ASF files
 +- SFTP protocol (via libssh)
 +- libx264: add ability to encode in YUVJ422P and YUVJ444P
 +- Fraps: use BT.709 colorspace by default for yuv, as reference fraps decoder does
 +- make decoding alpha optional for prores, ffv1 and vp6 by setting
 +  the skip_alpha flag.
 +- ladspa wrapper filter
 +- native VP9 decoder
 +- dpx parser
 +- max_error_rate parameter in ffmpeg
 +- PulseAudio output device
 +- ReplayGain scanner
 +- Enhanced Low Delay AAC (ER AAC ELD) decoding (no LD SBR support)
 +- Linux framebuffer output device
 +- HEVC decoder
 +- raw HEVC, HEVC in MOV/MP4, HEVC in Matroska, HEVC in MPEG-TS demuxing
 +- mergeplanes filter
 +
 +
 +version 2.0:
 +
 +- curves filter
  - reference-counting for AVFrame and AVPacket data
 -- avconv now fails when input options are used for output file
 +- ffmpeg now fails when input options are used for output file
    or vice versa
 -- new avconv options -filter_script and -filter_complex_script, which allow a
 +- support for Monkey's Audio versions from 3.93
 +- perms and aperms filters
 +- audio filtering support in ffplay
 +- 10% faster aac encoding on x86 and MIPS
 +- sine audio filter source
 +- WebP demuxing and decoding support
 +- new ffmpeg options -filter_script and -filter_complex_script, which allow a
    filtergraph description to be read from a file
 +- OpenCL support
 +- audio phaser filter
 +- separatefields filter
 +- libquvi demuxer
  - uniform options syntax across all filters
 +- telecine filter
  - new interlace filter
 -- JPEG 2000 decoder
 -- new asetpts filter (same as setpts, but for audio)
 -- new trim and atrim filters
 -- avconv -t and -ss (output-only) options are now sample-accurate when
 +- smptehdbars source
 +- inverse telecine filters (fieldmatch and decimate)
 +- colorbalance filter
 +- colorchannelmixer filter
 +- The matroska demuxer can now output proper verbatim ASS packets. It will
 +  become the default at the next libavformat major bump.
 +- decent native animated GIF encoding
 +- asetrate filter
 +- interleave filter
 +- timeline editing with filters
 +- vidstabdetect and vidstabtransform filters for video stabilization using
 +  the vid.stab library
 +- astats filter
 +- trim and atrim filters
 +- ffmpeg -t and -ss (output-only) options are now sample-accurate when
    transcoding audio
  - Matroska muxer can now put the index at the beginning of the file.
 -- avconv -deinterlace option removed, the yadif filter should be used instead
 +- extractplanes filter
 +- avectorscope filter
 +- ADPCM DTK decoder
 +- ADP demuxer
 +- RSD demuxer
 +- RedSpark demuxer
 +- ADPCM IMA Radical decoder
 +- zmq filters
 +- DCT denoiser filter (dctdnoiz)
 +- Wavelet denoiser filter ported from libmpcodecs as owdenoise (formerly "ow")
  - Apple Intermediate Codec decoder
  - Escape 130 video decoder
 +- FTP protocol support
 +- V4L2 output device
 +- 3D LUT filter (lut3d)
 +- SMPTE 302M audio encoder
  - support for slice multithreading in libavfilter
 +- Hald CLUT support (generation and filtering)
  - VC-1 interlaced B-frame support
  - support for WavPack muxing (raw and in Matroska)
 +- XVideo output device
 +- vignette filter
 +- True Audio (TTA) encoder
  - Go2Webinar decoder
 +- mcdeint filter ported from libmpcodecs
 +- sab filter ported from libmpcodecs
 +- ffprobe -show_chapters option
  - WavPack encoding through libwavpack
 -- Added the -n parameter to avconv
 -- RTMP seek support
 -- when transcoding with avconv (i.e. not streamcopying), -ss is now accurate
 -  even when used as an input option. Previous behavior can be restored with
 -  the -noaccurate_seek option.
 -- avconv -t option can now be used for inputs, to limit the duration of
 -  data read from an input file
 -- incomplete Voxware MetaSound decoder
 -- WebP decoder
 -- Error Resilient AAC syntax (ER AAC LC) decoding
 -- Low Delay AAC (ER AAC LD) decoding
 -- mux chapters in ASF files
 -- Opus in Ogg demuxing
 -- Enhanced Low Delay AAC (ER AAC ELD) decoding (no LD SBR support)
 -- F4V muxer
 -- HNM version 4 demuxer and video decoder
 -- HEVC decoder
 -- raw HEVC, HEVC in MOV/MP4, HEVC in Matroska, HEVC in MPEG-TS demuxing
 -- remove avplay -vismv option, which has not worked for a long time
 -- Live HDS muxer
 +- rotate filter
 +- spp filter ported from libmpcodecs
 +- libgme support
 +- psnr filter
  
  
 -version 9:
 -- av_basename and av_dirname
 -- adobe and limelight publisher authentication in RTMP
 +version 1.2:
 +
  - VDPAU hardware acceleration through normal hwaccel
  - SRTP support
 +- Error diffusion dither in Swscale
 +- Chained Ogg support
 +- Theora Midstream reconfiguration support
 +- EVRC decoder
 +- audio fade filter
 +- filtering audio with unknown channel layout
 +- allpass, bass, bandpass, bandreject, biquad, equalizer, highpass, lowpass
 +  and treble audio filter
 +- improved showspectrum filter, with multichannel support and sox-like colors
 +- histogram filter
 +- tee muxer
 +- il filter ported from libmpcodecs
 +- support ID3v2 tags in ASF files
 +- encrypted TTA stream decoding support
 +- RF64 support in WAV muxer
 +- noise filter ported from libmpcodecs
 +- Subtitles character encoding conversion
 +- blend filter
 +- stereo3d filter ported from libmpcodecs
 +
  
 +version 1.1:
  
 -version 9_beta3:
 -- ashowinfo audio filter
 +- stream disposition information printing in ffprobe
 +- filter for loudness analysis following EBU R128
 +- Opus encoder using libopus
 +- ffprobe -select_streams option
 +- Pinnacle TARGA CineWave YUV16 decoder
 +- TAK demuxer, decoder and parser
 +- DTS-HD demuxer
 +- remove -same_quant, it hasn't worked for years
 +- FFM2 support
 +- X-Face image encoder and decoder
  - 24-bit FLAC encoding
 -- audio volume filter
 -- deprecated the avconv -vol option. the volume filter is to be used instead.
  - multi-channel ALAC encoding up to 7.1
 -- TAK demuxer, parser, and decoder
 -- adaptive frame-level multithreading for H.264
 -
 -
 -version 9_beta2:
  - metadata (INFO tag) support in WAV muxer
 +- subtitles raw text decoder
  - support for building DLLs using MSVC
 -- remove avserver daemon mode
 +- LVF demuxer
 +- ffescape tool
 +- metadata (info chunk) support in CAF muxer
 +- field filter ported from libmpcodecs
 +- AVR demuxer
 +- geq filter ported from libmpcodecs
 +- remove ffserver daemon mode
 +- AST muxer/demuxer
 +- new expansion syntax for drawtext
 +- BRender PIX image decoder
 +- ffprobe -show_entries option
 +- ffprobe -sections option
 +- ADPCM IMA Dialogic decoder
 +- BRSTM demuxer
 +- animated GIF decoder and demuxer
 +- PVF demuxer
 +- subtitles filter
 +- IRCAM muxer/demuxer
 +- Paris Audio File demuxer
 +- Virtual concatenation demuxer
 +- VobSub demuxer
 +- JSON captions for TED talks decoding support
 +- SOX Resampler support in libswresample
 +- aselect filter
 +- SGI RLE 8-bit decoder
 +- Silicon Graphics Motion Video Compressor 1 & 2 decoder
 +- Silicon Graphics Movie demuxer
 +- apad filter
 +- Resolution & pixel format change support with multithreading for H.264
 +- documentation split into per-component manuals
 +- pp (postproc) filter ported from MPlayer
 +- NIST Sphere demuxer
 +- MPL2, VPlayer, MPlayer, AQTitle, PJS and SubViewer v1 subtitles demuxers and decoders
 +- Sony Wave64 muxer
 +- adobe and limelight publisher authentication in RTMP
 +- data: URI scheme
  - support building on the Plan 9 operating system
 -- ffv1: support version 1.3
 +- kerndeint filter ported from MPlayer
 +- histeq filter ported from VirtualDub
 +- Megalux Frame demuxer
 +- 012v decoder
 +- Improved AVC Intra decoding support
  
  
 -version 9_beta1:
 +version 1.0:
  
 -- XWD encoder and decoder
 -- Support for fragmentation in the mov/mp4 muxer
 -- ISMV (Smooth Streaming) muxer
 -- CDXL demuxer and decoder
 -- Apple ProRes encoder
 -- Sun Rasterfile Encoder
 -- remove libpostproc
 -- ID3v2 attached pictures reading and writing
 -- WMA Lossless decoder
 -- XBM encoder
 -- RealAudio Lossless decoder
 -- ZeroCodec decoder
 -- drop support for avconv without libavfilter
 -- add libavresample audio conversion library
 -- audio filters support in libavfilter and avconv
 -- add fps filter
 -- audio split filter
 -- audio mix filter
 -- avprobe output is now standard INI or JSON. The old format can still
 -  be used with -of old.
 +- INI and flat output in ffprobe
 +- Scene detection in libavfilter
  - Indeo Audio decoder
  - channelsplit audio filter
 +- setnsamples audio filter
 +- atempo filter
 +- ffprobe -show_data option
  - RTMPT protocol support
  - iLBC encoding/decoding via libilbc
  - Microsoft Screen 1 decoder
diff --cc configure
Simple merge
Simple merge
Simple merge
index 0000000,6d5b127..08ae549
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,589 +1,589 @@@
 - * This file is part of Libav.
+ /*
+  * Live HDS fragmenter
+  * Copyright (c) 2013 Martin Storsjo
+  *
 - * Libav is free software; you can redistribute it and/or
++ * This file is part of FFmpeg.
+  *
 - * Libav is distributed in the hope that it will be useful,
++ * FFmpeg is free software; you can redistribute it and/or
+  * modify it under the terms of the GNU Lesser General Public
+  * License as published by the Free Software Foundation; either
+  * version 2.1 of the License, or (at your option) any later version.
+  *
 - * License along with Libav; if not, write to the Free Software
++ * FFmpeg is distributed in the hope that it will be useful,
+  * but WITHOUT ANY WARRANTY; without even the implied warranty of
+  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  * Lesser General Public License for more details.
+  *
+  * You should have received a copy of the GNU Lesser General Public
++ * License along with FFmpeg; if not, write to the Free Software
+  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+  */
+ #include "config.h"
+ #include <float.h>
+ #if HAVE_UNISTD_H
+ #include <unistd.h>
+ #endif
+ #include "avformat.h"
+ #include "internal.h"
+ #include "os_support.h"
+ #include "libavutil/avstring.h"
+ #include "libavutil/base64.h"
+ #include "libavutil/intreadwrite.h"
+ #include "libavutil/mathematics.h"
+ #include "libavutil/opt.h"
+ typedef struct Fragment {
+     char file[1024];
+     int64_t start_time, duration;
+     int n;
+ } Fragment;
+ typedef struct OutputStream {
+     int bitrate;
+     int first_stream;
+     AVFormatContext *ctx;
+     int ctx_inited;
+     uint8_t iobuf[32768];
+     char temp_filename[1024];
+     int64_t frag_start_ts, last_ts;
+     AVIOContext *out;
+     int packets_written;
+     int nb_fragments, fragments_size, fragment_index;
+     Fragment **fragments;
+     int has_audio, has_video;
+     uint8_t *metadata;
+     int metadata_size;
+     uint8_t *extra_packets[2];
+     int extra_packet_sizes[2];
+     int nb_extra_packets;
+ } OutputStream;
+ typedef struct HDSContext {
+     const AVClass *class;  /* Class for private options. */
+     int window_size;
+     int extra_window_size;
+     int min_frag_duration;
+     int remove_at_exit;
+     OutputStream *streams;
+     int nb_streams;
+ } HDSContext;
+ static int parse_header(OutputStream *os, const uint8_t *buf, int buf_size)
+ {
+     if (buf_size < 13)
+         return AVERROR_INVALIDDATA;
+     if (memcmp(buf, "FLV", 3))
+         return AVERROR_INVALIDDATA;
+     buf      += 13;
+     buf_size -= 13;
+     while (buf_size >= 11 + 4) {
+         int type = buf[0];
+         int size = AV_RB24(&buf[1]) + 11 + 4;
+         if (size > buf_size)
+             return AVERROR_INVALIDDATA;
+         if (type == 8 || type == 9) {
+             if (os->nb_extra_packets > FF_ARRAY_ELEMS(os->extra_packets))
+                 return AVERROR_INVALIDDATA;
+             os->extra_packet_sizes[os->nb_extra_packets] = size;
+             os->extra_packets[os->nb_extra_packets] = av_malloc(size);
+             if (!os->extra_packets[os->nb_extra_packets])
+                 return AVERROR(ENOMEM);
+             memcpy(os->extra_packets[os->nb_extra_packets], buf, size);
+             os->nb_extra_packets++;
+         } else if (type == 0x12) {
+             if (os->metadata)
+                 return AVERROR_INVALIDDATA;
+             os->metadata_size = size - 11 - 4;
+             os->metadata      = av_malloc(os->metadata_size);
+             if (!os->metadata)
+                 return AVERROR(ENOMEM);
+             memcpy(os->metadata, buf + 11, os->metadata_size);
+         }
+         buf      += size;
+         buf_size -= size;
+     }
+     if (!os->metadata)
+         return AVERROR_INVALIDDATA;
+     return 0;
+ }
+ static int hds_write(void *opaque, uint8_t *buf, int buf_size)
+ {
+     OutputStream *os = opaque;
+     if (os->out) {
+         avio_write(os->out, buf, buf_size);
+     } else {
+         if (!os->metadata_size) {
+             int ret;
+             // Assuming the IO buffer is large enough to fit the
+             // FLV header and all metadata and extradata packets
+             if ((ret = parse_header(os, buf, buf_size)) < 0)
+                 return ret;
+         }
+     }
+     return buf_size;
+ }
+ static void hds_free(AVFormatContext *s)
+ {
+     HDSContext *c = s->priv_data;
+     int i, j;
+     if (!c->streams)
+         return;
+     for (i = 0; i < s->nb_streams; i++) {
+         OutputStream *os = &c->streams[i];
+         if (os->out)
+             avio_close(os->out);
+         os->out = NULL;
+         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)
+             avformat_free_context(os->ctx);
+         av_free(os->metadata);
+         for (j = 0; j < os->nb_extra_packets; j++)
+             av_free(os->extra_packets[j]);
+         for (j = 0; j < os->nb_fragments; j++)
+             av_free(os->fragments[j]);
+         av_free(os->fragments);
+     }
+     av_freep(&c->streams);
+ }
+ static int write_manifest(AVFormatContext *s, int final)
+ {
+     HDSContext *c = s->priv_data;
+     AVIOContext *out;
+     char filename[1024], temp_filename[1024];
+     int ret, i;
+     float duration = 0;
+     if (c->nb_streams > 0)
+         duration = c->streams[0].last_ts * av_q2d(s->streams[0]->time_base);
+     snprintf(filename, sizeof(filename), "%s/index.f4m", s->filename);
+     snprintf(temp_filename, sizeof(temp_filename), "%s/index.f4m.tmp", s->filename);
+     ret = avio_open2(&out, temp_filename, AVIO_FLAG_WRITE,
+                      &s->interrupt_callback, NULL);
+     if (ret < 0) {
+         av_log(s, AV_LOG_ERROR, "Unable to open %s for writing\n", filename);
+         return ret;
+     }
+     avio_printf(out, "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
+     avio_printf(out, "<manifest xmlns=\"http://ns.adobe.com/f4m/1.0\">\n");
+     avio_printf(out, "\t<id>%s</id>\n", av_basename(s->filename));
+     avio_printf(out, "\t<streamType>%s</streamType>\n",
+                      final ? "recorded" : "live");
+     avio_printf(out, "\t<deliveryType>streaming</deliveryType>\n");
+     if (final)
+         avio_printf(out, "\t<duration>%f</duration>\n", duration);
+     for (i = 0; i < c->nb_streams; i++) {
+         OutputStream *os = &c->streams[i];
+         int b64_size = AV_BASE64_SIZE(os->metadata_size);
+         char *base64 = av_malloc(b64_size);
+         if (!base64) {
+             avio_close(out);
+             return AVERROR(ENOMEM);
+         }
+         av_base64_encode(base64, b64_size, os->metadata, os->metadata_size);
+         avio_printf(out, "\t<bootstrapInfo profile=\"named\" url=\"stream%d.abst\" id=\"bootstrap%d\" />\n", i, i);
+         avio_printf(out, "\t<media bitrate=\"%d\" url=\"stream%d\" bootstrapInfoId=\"bootstrap%d\">\n", os->bitrate/1000, i, i);
+         avio_printf(out, "\t\t<metadata>%s</metadata>\n", base64);
+         avio_printf(out, "\t</media>\n");
+         av_free(base64);
+     }
+     avio_printf(out, "</manifest>\n");
+     avio_flush(out);
+     avio_close(out);
+     rename(temp_filename, filename);
+     return 0;
+ }
+ static void update_size(AVIOContext *out, int64_t pos)
+ {
+     int64_t end = avio_tell(out);
+     avio_seek(out, pos, SEEK_SET);
+     avio_wb32(out, end - pos);
+     avio_seek(out, end, SEEK_SET);
+ }
+ /* Note, the .abst files need to be served with the "binary/octet"
+  * mime type, otherwise at least the OSMF player can easily fail
+  * with "stream not found" when polling for the next fragment. */
+ static int write_abst(AVFormatContext *s, OutputStream *os, int final)
+ {
+     HDSContext *c = s->priv_data;
+     AVIOContext *out;
+     char filename[1024], temp_filename[1024];
+     int i, ret;
+     int64_t asrt_pos, afrt_pos;
+     int start = 0, fragments;
+     int index = s->streams[os->first_stream]->id;
+     int64_t cur_media_time = 0;
+     if (c->window_size)
+         start = FFMAX(os->nb_fragments - c->window_size, 0);
+     fragments = os->nb_fragments - start;
+     if (final)
+         cur_media_time = os->last_ts;
+     else if (os->nb_fragments)
+         cur_media_time = os->fragments[os->nb_fragments - 1]->start_time;
+     snprintf(filename, sizeof(filename),
+              "%s/stream%d.abst", s->filename, index);
+     snprintf(temp_filename, sizeof(temp_filename),
+              "%s/stream%d.abst.tmp", s->filename, index);
+     ret = avio_open2(&out, temp_filename, AVIO_FLAG_WRITE,
+                      &s->interrupt_callback, NULL);
+     if (ret < 0) {
+         av_log(s, AV_LOG_ERROR, "Unable to open %s for writing\n", temp_filename);
+         return ret;
+     }
+     avio_wb32(out, 0); // abst size
+     avio_wl32(out, MKTAG('a','b','s','t'));
+     avio_wb32(out, 0); // version + flags
+     avio_wb32(out, os->fragment_index - 1); // BootstrapinfoVersion
+     avio_w8(out, final ? 0 : 0x20); // profile, live, update
+     avio_wb32(out, 1000); // timescale
+     avio_wb64(out, cur_media_time);
+     avio_wb64(out, 0); // SmpteTimeCodeOffset
+     avio_w8(out, 0); // MovieIdentifer (null string)
+     avio_w8(out, 0); // ServerEntryCount
+     avio_w8(out, 0); // QualityEntryCount
+     avio_w8(out, 0); // DrmData (null string)
+     avio_w8(out, 0); // MetaData (null string)
+     avio_w8(out, 1); // SegmentRunTableCount
+     asrt_pos = avio_tell(out);
+     avio_wb32(out, 0); // asrt size
+     avio_wl32(out, MKTAG('a','s','r','t'));
+     avio_wb32(out, 0); // version + flags
+     avio_w8(out, 0); // QualityEntryCount
+     avio_wb32(out, 1); // SegmentRunEntryCount
+     avio_wb32(out, 1); // FirstSegment
+     avio_wb32(out, final ? (os->fragment_index - 1) : 0xffffffff); // FragmentsPerSegment
+     update_size(out, asrt_pos);
+     avio_w8(out, 1); // FragmentRunTableCount
+     afrt_pos = avio_tell(out);
+     avio_wb32(out, 0); // afrt size
+     avio_wl32(out, MKTAG('a','f','r','t'));
+     avio_wb32(out, 0); // version + flags
+     avio_wb32(out, 1000); // timescale
+     avio_w8(out, 0); // QualityEntryCount
+     avio_wb32(out, fragments); // FragmentRunEntryCount
+     for (i = start; i < os->nb_fragments; i++) {
+         avio_wb32(out, os->fragments[i]->n);
+         avio_wb64(out, os->fragments[i]->start_time);
+         avio_wb32(out, os->fragments[i]->duration);
+     }
+     update_size(out, afrt_pos);
+     update_size(out, 0);
+     avio_close(out);
+     rename(temp_filename, filename);
+     return 0;
+ }
+ static int init_file(AVFormatContext *s, OutputStream *os, int64_t start_ts)
+ {
+     int ret, i;
+     ret = avio_open2(&os->out, os->temp_filename, AVIO_FLAG_WRITE,
+                      &s->interrupt_callback, NULL);
+     if (ret < 0)
+         return ret;
+     avio_wb32(os->out, 0);
+     avio_wl32(os->out, MKTAG('m','d','a','t'));
+     for (i = 0; i < os->nb_extra_packets; i++) {
+         AV_WB24(os->extra_packets[i] + 4, start_ts);
+         os->extra_packets[i][7] = (start_ts >> 24) & 0x7f;
+         avio_write(os->out, os->extra_packets[i], os->extra_packet_sizes[i]);
+     }
+     return 0;
+ }
+ static void close_file(OutputStream *os)
+ {
+     int64_t pos = avio_tell(os->out);
+     avio_seek(os->out, 0, SEEK_SET);
+     avio_wb32(os->out, pos);
+     avio_flush(os->out);
+     avio_close(os->out);
+     os->out = NULL;
+ }
+ static int hds_write_header(AVFormatContext *s)
+ {
+     HDSContext *c = s->priv_data;
+     int ret = 0, i;
+     AVOutputFormat *oformat;
+     mkdir(s->filename, 0777);
+     oformat = av_guess_format("flv", NULL, NULL);
+     if (!oformat) {
+         ret = AVERROR_MUXER_NOT_FOUND;
+         goto fail;
+     }
+     c->streams = av_mallocz(sizeof(*c->streams) * s->nb_streams);
+     if (!c->streams) {
+         ret = AVERROR(ENOMEM);
+         goto fail;
+     }
+     for (i = 0; i < s->nb_streams; i++) {
+         OutputStream *os = &c->streams[c->nb_streams];
+         AVFormatContext *ctx;
+         AVStream *st = s->streams[i];
+         if (!st->codec->bit_rate) {
+             av_log(s, AV_LOG_ERROR, "No bit rate set for stream %d\n", i);
+             ret = AVERROR(EINVAL);
+             goto fail;
+         }
+         if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
+             if (os->has_video) {
+                 c->nb_streams++;
+                 os++;
+             }
+             os->has_video = 1;
+         } else if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO) {
+             if (os->has_audio) {
+                 c->nb_streams++;
+                 os++;
+             }
+             os->has_audio = 1;
+         } else {
+             av_log(s, AV_LOG_ERROR, "Unsupported stream type in stream %d\n", i);
+             ret = AVERROR(EINVAL);
+             goto fail;
+         }
+         os->bitrate += s->streams[i]->codec->bit_rate;
+         if (!os->ctx) {
+             os->first_stream = i;
+             ctx = avformat_alloc_context();
+             if (!ctx) {
+                 ret = AVERROR(ENOMEM);
+                 goto fail;
+             }
+             os->ctx = ctx;
+             ctx->oformat = oformat;
+             ctx->interrupt_callback = s->interrupt_callback;
+             ctx->pb = avio_alloc_context(os->iobuf, sizeof(os->iobuf),
+                                          AVIO_FLAG_WRITE, os,
+                                          NULL, hds_write, NULL);
+             if (!ctx->pb) {
+                 ret = AVERROR(ENOMEM);
+                 goto fail;
+             }
+         } else {
+             ctx = os->ctx;
+         }
+         s->streams[i]->id = c->nb_streams;
+         if (!(st = avformat_new_stream(ctx, NULL))) {
+             ret = AVERROR(ENOMEM);
+             goto fail;
+         }
+         avcodec_copy_context(st->codec, s->streams[i]->codec);
+         st->sample_aspect_ratio = s->streams[i]->sample_aspect_ratio;
+     }
+     if (c->streams[c->nb_streams].ctx)
+         c->nb_streams++;
+     for (i = 0; i < c->nb_streams; i++) {
+         OutputStream *os = &c->streams[i];
+         int j;
+         if ((ret = avformat_write_header(os->ctx, NULL)) < 0) {
+              goto fail;
+         }
+         os->ctx_inited = 1;
+         avio_flush(os->ctx->pb);
+         for (j = 0; j < os->ctx->nb_streams; j++)
+             s->streams[os->first_stream + j]->time_base = os->ctx->streams[j]->time_base;
+         snprintf(os->temp_filename, sizeof(os->temp_filename),
+                  "%s/stream%d_temp", s->filename, i);
+         init_file(s, os, 0);
+         if (!os->has_video && c->min_frag_duration <= 0) {
+             av_log(s, AV_LOG_WARNING,
+                    "No video stream in output stream %d and no min frag duration set\n", i);
+             ret = AVERROR(EINVAL);
+         }
+         os->fragment_index = 1;
+         write_abst(s, os, 0);
+     }
+     ret = write_manifest(s, 0);
+ fail:
+     if (ret)
+         hds_free(s);
+     return ret;
+ }
+ static int add_fragment(OutputStream *os, const char *file,
+                         int64_t start_time, int64_t duration)
+ {
+     Fragment *frag;
+     if (duration == 0)
+         duration = 1;
+     if (os->nb_fragments >= os->fragments_size) {
+         int ret;
+         os->fragments_size = (os->fragments_size + 1) * 2;
+         if ((ret = av_reallocp_array(&os->fragments, os->fragments_size,
+                                      sizeof(*os->fragments))) < 0) {
+             os->fragments_size = 0;
+             os->nb_fragments   = 0;
+             return ret;
+         }
+     }
+     frag = av_mallocz(sizeof(*frag));
+     if (!frag)
+         return AVERROR(ENOMEM);
+     av_strlcpy(frag->file, file, sizeof(frag->file));
+     frag->start_time = start_time;
+     frag->duration   = duration;
+     frag->n          = os->fragment_index;
+     os->fragments[os->nb_fragments++] = frag;
+     os->fragment_index++;
+     return 0;
+ }
+ static int hds_flush(AVFormatContext *s, OutputStream *os, int final,
+                      int64_t end_ts)
+ {
+     HDSContext *c = s->priv_data;
+     int i, ret = 0;
+     char target_filename[1024];
+     int index = s->streams[os->first_stream]->id;
+     if (!os->packets_written)
+         return 0;
+     avio_flush(os->ctx->pb);
+     os->packets_written = 0;
+     close_file(os);
+     snprintf(target_filename, sizeof(target_filename),
+              "%s/stream%dSeg1-Frag%d", s->filename, index, os->fragment_index);
+     rename(os->temp_filename, target_filename);
+     add_fragment(os, target_filename, os->frag_start_ts, end_ts - os->frag_start_ts);
+     if (!final) {
+         ret = init_file(s, os, end_ts);
+         if (ret < 0)
+             return ret;
+     }
+     if (c->window_size || (final && c->remove_at_exit)) {
+         int remove = os->nb_fragments - c->window_size - c->extra_window_size;
+         if (final && c->remove_at_exit)
+             remove = os->nb_fragments;
+         if (remove > 0) {
+             for (i = 0; i < remove; i++) {
+                 unlink(os->fragments[i]->file);
+                 av_free(os->fragments[i]);
+             }
+             os->nb_fragments -= remove;
+             memmove(os->fragments, os->fragments + remove,
+                     os->nb_fragments * sizeof(*os->fragments));
+         }
+     }
+     if (ret >= 0)
+         ret = write_abst(s, os, final);
+     return ret;
+ }
+ static int hds_write_packet(AVFormatContext *s, AVPacket *pkt)
+ {
+     HDSContext *c = s->priv_data;
+     AVStream *st = s->streams[pkt->stream_index];
+     OutputStream *os = &c->streams[s->streams[pkt->stream_index]->id];
+     int64_t end_dts = (os->fragment_index) * c->min_frag_duration;
+     int ret;
+     if (st->first_dts == AV_NOPTS_VALUE)
+         st->first_dts = pkt->dts;
+     if ((!os->has_video || st->codec->codec_type == AVMEDIA_TYPE_VIDEO) &&
+         av_compare_ts(pkt->dts - st->first_dts, st->time_base,
+                       end_dts, AV_TIME_BASE_Q) >= 0 &&
+         pkt->flags & AV_PKT_FLAG_KEY && os->packets_written) {
+         if ((ret = hds_flush(s, os, 0, pkt->dts)) < 0)
+             return ret;
+     }
+     // Note, these fragment start timestamps, that represent a whole
+     // OutputStream, assume all streams in it have the same time base.
+     if (!os->packets_written)
+         os->frag_start_ts = pkt->dts;
+     os->last_ts = pkt->dts;
+     os->packets_written++;
+     return ff_write_chained(os->ctx, pkt->stream_index - os->first_stream, pkt, s);
+ }
+ static int hds_write_trailer(AVFormatContext *s)
+ {
+     HDSContext *c = s->priv_data;
+     int i;
+     for (i = 0; i < c->nb_streams; i++)
+         hds_flush(s, &c->streams[i], 1, c->streams[i].last_ts);
+     write_manifest(s, 1);
+     if (c->remove_at_exit) {
+         char filename[1024];
+         snprintf(filename, sizeof(filename), "%s/index.f4m", s->filename);
+         unlink(filename);
+         for (i = 0; i < c->nb_streams; i++) {
+             snprintf(filename, sizeof(filename), "%s/stream%d.abst", s->filename, i);
+             unlink(filename);
+         }
+         rmdir(s->filename);
+     }
+     hds_free(s);
+     return 0;
+ }
+ #define OFFSET(x) offsetof(HDSContext, x)
+ #define E AV_OPT_FLAG_ENCODING_PARAM
+ static const AVOption options[] = {
+     { "window_size", "number of fragments kept in the manifest", OFFSET(window_size), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, E },
+     { "extra_window_size", "number of fragments kept outside of the manifest before removing from disk", OFFSET(extra_window_size), AV_OPT_TYPE_INT, { .i64 = 5 }, 0, INT_MAX, E },
+     { "min_frag_duration", "minimum fragment duration (in microseconds)", OFFSET(min_frag_duration), AV_OPT_TYPE_INT64, { .i64 = 10000000 }, 0, INT_MAX, E },
+     { "remove_at_exit", "remove all fragments when finished", OFFSET(remove_at_exit), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, E },
+     { NULL },
+ };
+ static const AVClass hds_class = {
+     .class_name = "HDS muxer",
+     .item_name  = av_default_item_name,
+     .option     = options,
+     .version    = LIBAVUTIL_VERSION_INT,
+ };
+ AVOutputFormat ff_hds_muxer = {
+     .name           = "hds",
+     .long_name      = NULL_IF_CONFIG_SMALL("HDS Muxer"),
+     .priv_data_size = sizeof(HDSContext),
+     .audio_codec    = AV_CODEC_ID_AAC,
+     .video_codec    = AV_CODEC_ID_H264,
+     .flags          = AVFMT_GLOBALHEADER | AVFMT_NOFILE,
+     .write_header   = hds_write_header,
+     .write_packet   = hds_write_packet,
+     .write_trailer  = hds_write_trailer,
+     .priv_class     = &hds_class,
+ };
@@@ -30,8 -30,8 +30,8 @@@
  #include "libavutil/avutil.h"
  
  #define LIBAVFORMAT_VERSION_MAJOR 55
- #define LIBAVFORMAT_VERSION_MINOR 20
- #define LIBAVFORMAT_VERSION_MICRO 105
 -#define LIBAVFORMAT_VERSION_MINOR 10
 -#define LIBAVFORMAT_VERSION_MICRO  0
++#define LIBAVFORMAT_VERSION_MINOR 21
++#define LIBAVFORMAT_VERSION_MICRO 100
  
  #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \
                                                 LIBAVFORMAT_VERSION_MINOR, \