Merge commit 'fe5e6e34c05e274f98528be4f77f3c474473f977'
authorMichael Niedermayer <michaelni@gmx.at>
Mon, 17 Nov 2014 21:07:11 +0000 (22:07 +0100)
committerMichael Niedermayer <michaelni@gmx.at>
Mon, 17 Nov 2014 21:13:50 +0000 (22:13 +0100)
* commit 'fe5e6e34c05e274f98528be4f77f3c474473f977':
  lavf: Add an MPEG-DASH ISOFF segmenting muxer

Conflicts:
Changelog
libavformat/Makefile
libavformat/allformats.c
libavformat/version.h

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

diff --cc Changelog
+++ b/Changelog
@@@ -2,316 -2,154 +2,316 @@@ Entries are sorted chronologically fro
  releases are sorted from youngest to oldest.
  
  version <next>:
 -- aliases and defaults for Ogg subtypes (opus, spx)
 -- HEVC/H.265 RTP payload format (draft v6) packetizer and depacketizer
 -- avplay now exits by default at the end of playback
 +- HEVC/H.265 RTP payload format (draft v6) packetizer
 +- SUP/PGS subtitle demuxer
 +- ffprobe -show_pixel_formats option
 +- CAST128 symmetric block cipher, ECB mode
 +- STL subtitle demuxer and decoder
 +- libutvideo YUV 4:2:2 10bit support
  - XCB-based screen-grabber
- - creating DASH compatible fragmented MP4
 +- UDP-Lite support (RFC 3828)
 +- xBR scaling filter
 +- AVFoundation screen capturing support
 +- ffserver supports codec private options
+ - creating DASH compatible fragmented MP4, MPEG-DASH segmenting muxer
  
 +version 2.4:
 +- Icecast protocol
 +- ported lenscorrection filter from frei0r filter
 +- large optimizations in dctdnoiz to make it usable
 +- ICY metadata are now requested by default with the HTTP protocol
 +- support for using metadata in stream specifiers in fftools
 +- LZMA compression support in TIFF decoder
 +- support for H.261 RTP payload format (RFC 4587)
 +- HEVC/H.265 RTP payload format (draft v6) depacketizer
 +- added codecview filter to visualize information exported by some codecs
 +- Matroska 3D support thorugh side data
 +- HTML generation using texi2html is deprecated in favor of makeinfo/texi2any
 +- silenceremove filter
  
 -version 11:
 -- libx265 encoder
 +
 +version 2.3:
 +- AC3 fixed-point decoding
  - shuffleplanes filter
 +- subfile protocol
 +- Phantom Cine demuxer
  - replaygain data export
 +- VP7 video decoder
  - Alias PIX image encoder and decoder
 -- BRender PIX image decoder
 -- Amazing Studio PAF playback support
 -- XBM decoder
 -- BMP standalone parser
 -- OpenEXR image decoder
 -- support encoding and decoding 4-channel SGI images
 +- Improvements to the BRender PIX image decoder
 +- Improvements to the XBM decoder
 +- QTKit input device
 +- improvements to OpenEXR image decoder
  - support decoding 16-bit RLE SGI images
 -- VP7 video decoder
 -- LucasArts SMUSH SANM video decoder
 -- LucasArts SMUSH VIMA audio decoder (ADPCM)
 -- LucasArts SMUSH demuxer
 -- MP2 encoding via TwoLAME
 -- asettb filter
 -- Silicon Graphics RLE 8-bit video decoder
 -- Silicon Graphics Motion Video Compressor 1 & 2 decoder
 -- Silicon Graphics Movie demuxer
 +- GDI screen grabbing for Windows
 +- alternative rendition support for HTTP Live Streaming
 +- AVFoundation input device
 +- Direct Stream Digital (DSD) decoder
 +- Magic Lantern Video (MLV) demuxer
  - On2 AVC (Audio for Video) decoder
 -- support for decoding through DXVA2 in avconv
 +- support for decoding through DXVA2 in ffmpeg
  - libbs2b-based stereo-to-binaural audio filter
 +- libx264 reference frames count limiting depending on level
  - native Opus decoder
  - display matrix export and rotation API
 -- drop avserver, it was unmaintained for years and largely broken
 -- Icecast protocol
 -- request Icecast metadata by default
 -- support for using metadata in stream specifiers in avtools
 -- Matroska 3D support
 +- WebVTT encoder
 +- showcqt multimedia filter
 +- zoompan filter
 +- signalstats filter
 +- hqx filter (hq2x, hq3x, hq4x)
 +- flanger filter
 +- Image format auto-detection
 +- LRC demuxer and muxer
 +- Samba protocol (via libsmbclient)
 +- WebM DASH Manifest muxer
 +- libfribidi support in drawtext
  
  
 -version 10:
 -- av_strnstr
 -- support ID3v2 tags in ASF files
 +version 2.2:
 +
 +- HNM version 4 demuxer and video decoder
 +- Live HDS muxer
 +- setsar/setdar filters now support variables in ratio expressions
 +- elbg filter
 +- string validation in ffprobe
 +- support for decoding through VDPAU in ffmpeg (the -hwaccel option)
 +- complete Voxware MetaSound decoder
 +- remove mp3_header_compress bitstream filter
 +- Windows resource files for shared libraries
 +- aeval filter
 +- stereoscopic 3d metadata handling
 +- WebP encoding via libwebp
 +- ATRAC3+ decoder
 +- VP8 in Ogg demuxing
 +- side & metadata support in NUT
 +- framepack filter
 +- XYZ12 rawvideo support in NUT
 +- Exif metadata support in WebP decoder
 +- OpenGL device
 +- Use metadata_header_padding to control padding in ID3 tags (currently used in
 +  MP3, AIFF, and OMA files), FLAC header, and the AVI "junk" block.
 +- Mirillis FIC video decoder
 +- Support DNx444
 +- libx265 encoder
 +- dejudder filter
 +- Autodetect VDA like all other hardware accelerations
 +- aliases and defaults for Ogg subtypes (opus, spx)
 +
 +
 +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
 -- 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
 +- 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
  - interlace filter
 -- JPEG 2000 decoder
 -- asetpts filter (same as setpts, but for audio)
 +- 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
 -- avconv -t and -ss (output-only) options are now sample-accurate when
 +- 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
 -- 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
 -- setsar/setdar filters now support variables in ratio expressions
 -- dar variable in the scale filter now returns the actual DAR (i.e. a * sar)
 -- VP9 decoder
 -- support for decoding through VDPAU in avconv (the -hwaccel option)
 -- remove mp3_header_(de)compress bitstream filters
 -- stereoscopic 3d metadata handling
 -- png standalone parser
 -- WebP encoding via libwebp
 -- ATRAC3+ decoder
 -- framepack filter
 -- Mirillis FIC video decoder
 -- Support DNx444
 -- compand audio filter
 +- 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 9_beta3:
 -- ashowinfo audio filter
 +version 1.1:
 +
 +- 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 / Silicon Graphics RLE 8-bit video 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
+++ b/configure
@@@ -2397,9 -2039,8 +2397,10 @@@ avi_muxer_select="riffenc
  avisynth_demuxer_deps="avisynth"
  avisynth_demuxer_select="riffdec"
  caf_demuxer_select="riffdec"
+ dash_muxer_select="mp4_muxer"
  dirac_demuxer_select="dirac_parser"
 +dts_demuxer_select="dca_parser"
 +dtshd_demuxer_select="dca_parser"
  dv_demuxer_select="dvprofile"
  dv_muxer_select="dvprofile"
  dxa_demuxer_select="riffdec"
@@@ -109,11 -90,8 +109,12 @@@ OBJS-$(CONFIG_CAVSVIDEO_DEMUXER
  OBJS-$(CONFIG_CAVSVIDEO_MUXER)           += rawenc.o
  OBJS-$(CONFIG_CDG_DEMUXER)               += cdg.o
  OBJS-$(CONFIG_CDXL_DEMUXER)              += cdxl.o
 +OBJS-$(CONFIG_CINE_DEMUXER)              += cinedec.o
 +OBJS-$(CONFIG_CONCAT_DEMUXER)            += concatdec.o
  OBJS-$(CONFIG_CRC_MUXER)                 += crcenc.o
 +OBJS-$(CONFIG_DATA_DEMUXER)              += rawdec.o
 +OBJS-$(CONFIG_DATA_MUXER)                += rawdec.o
+ OBJS-$(CONFIG_DASH_MUXER)                += dashenc.o isom.o
  OBJS-$(CONFIG_DAUD_DEMUXER)              += dauddec.o
  OBJS-$(CONFIG_DAUD_MUXER)                += daudenc.o
  OBJS-$(CONFIG_DFA_DEMUXER)               += dfa.o
@@@ -98,10 -87,8 +98,11 @@@ void av_register_all(void
      REGISTER_MUXDEMUX(CAVSVIDEO,        cavsvideo);
      REGISTER_DEMUXER (CDG,              cdg);
      REGISTER_DEMUXER (CDXL,             cdxl);
 +    REGISTER_DEMUXER (CINE,             cine);
 +    REGISTER_DEMUXER (CONCAT,           concat);
      REGISTER_MUXER   (CRC,              crc);
+     REGISTER_MUXER   (DASH,             dash);
 +    REGISTER_MUXDEMUX(DATA,             data);
      REGISTER_MUXDEMUX(DAUD,             daud);
      REGISTER_DEMUXER (DFA,              dfa);
      REGISTER_MUXDEMUX(DIRAC,            dirac);
index 0000000,cacaf92..4f972b3
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,773 +1,773 @@@
 - * This file is part of Libav.
+ /*
+  * MPEG-DASH ISO BMFF segmenter
+  * Copyright (c) 2014 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
 -    return ff_rename(temp_filename, s->filename);
++ * 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"
+ #if HAVE_UNISTD_H
+ #include <unistd.h>
+ #endif
+ #include "libavutil/avstring.h"
+ #include "libavutil/intreadwrite.h"
+ #include "libavutil/mathematics.h"
+ #include "libavutil/opt.h"
+ #include "libavutil/time_internal.h"
+ #include "avc.h"
+ #include "avformat.h"
+ #include "avio_internal.h"
+ #include "internal.h"
+ #include "isom.h"
+ #include "os_support.h"
+ #include "url.h"
+ typedef struct Segment {
+     char file[1024];
+     int64_t start_pos;
+     int range_length, index_length;
+     int64_t time;
+     int duration;
+     int n;
+ } Segment;
+ typedef struct OutputStream {
+     AVFormatContext *ctx;
+     int ctx_inited;
+     uint8_t iobuf[32768];
+     URLContext *out;
+     int packets_written;
+     char initfile[1024];
+     int64_t init_start_pos;
+     int init_range_length;
+     int nb_segments, segments_size, segment_index;
+     Segment **segments;
+     int64_t first_dts, start_dts, end_dts;
+     char codec_str[100];
+ } OutputStream;
+ typedef struct DASHContext {
+     const AVClass *class;  /* Class for private options. */
+     int window_size;
+     int extra_window_size;
+     int min_seg_duration;
+     int remove_at_exit;
+     int use_template;
+     int use_timeline;
+     int single_file;
+     OutputStream *streams;
+     int has_video, has_audio;
+     int nb_segments;
+     int last_duration;
+     int total_duration;
+     char availability_start_time[100];
+     char dirname[1024];
+ } DASHContext;
+ static int dash_write(void *opaque, uint8_t *buf, int buf_size)
+ {
+     OutputStream *os = opaque;
+     if (os->out)
+         ffurl_write(os->out, buf, buf_size);
+     return buf_size;
+ }
+ // RFC 6381
+ static void set_codec_str(AVFormatContext *s, AVCodecContext *codec,
+                           char *str, int size)
+ {
+     const AVCodecTag *tags[2] = { NULL, NULL };
+     uint32_t tag;
+     if (codec->codec_type == AVMEDIA_TYPE_VIDEO)
+         tags[0] = ff_codec_movvideo_tags;
+     else if (codec->codec_type == AVMEDIA_TYPE_AUDIO)
+         tags[0] = ff_codec_movaudio_tags;
+     else
+         return;
+     tag = av_codec_get_tag(tags, codec->codec_id);
+     if (!tag)
+         return;
+     if (size < 5)
+         return;
+     AV_WL32(str, tag);
+     str[4] = '\0';
+     if (!strcmp(str, "mp4a") || !strcmp(str, "mp4v")) {
+         uint32_t oti;
+         tags[0] = ff_mp4_obj_type;
+         oti = av_codec_get_tag(tags, codec->codec_id);
+         if (oti)
+             av_strlcatf(str, size, ".%02x", oti);
+         else
+             return;
+         if (tag == MKTAG('m', 'p', '4', 'a')) {
+             if (codec->extradata_size >= 2) {
+                 int aot = codec->extradata[0] >> 3;
+                 if (aot == 31)
+                     aot = ((AV_RB16(codec->extradata) >> 5) & 0x3f) + 32;
+                 av_strlcatf(str, size, ".%d", aot);
+             }
+         } else if (tag == MKTAG('m', 'p', '4', 'v')) {
+             // Unimplemented, should output ProfileLevelIndication as a decimal number
+             av_log(s, AV_LOG_WARNING, "Incomplete RFC 6381 codec string for mp4v\n");
+         }
+     } else if (!strcmp(str, "avc1")) {
+         uint8_t *tmpbuf = NULL;
+         uint8_t *extradata = codec->extradata;
+         int extradata_size = codec->extradata_size;
+         if (!extradata_size)
+             return;
+         if (extradata[0] != 1) {
+             AVIOContext *pb;
+             if (avio_open_dyn_buf(&pb) < 0)
+                 return;
+             if (ff_isom_write_avcc(pb, extradata, extradata_size) < 0) {
+                 avio_close_dyn_buf(pb, &tmpbuf);
+                 av_free(tmpbuf);
+                 return;
+             }
+             extradata_size = avio_close_dyn_buf(pb, &extradata);
+             tmpbuf = extradata;
+         }
+         if (extradata_size >= 4)
+             av_strlcatf(str, size, ".%02x%02x%02x",
+                         extradata[1], extradata[2], extradata[3]);
+         av_free(tmpbuf);
+     }
+ }
+ static void dash_free(AVFormatContext *s)
+ {
+     DASHContext *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->ctx && os->ctx_inited)
+             av_write_trailer(os->ctx);
+         if (os->ctx && os->ctx->pb)
+             av_free(os->ctx->pb);
+         ffurl_close(os->out);
+         os->out =  NULL;
+         if (os->ctx)
+             avformat_free_context(os->ctx);
+         for (j = 0; j < os->nb_segments; j++)
+             av_free(os->segments[j]);
+         av_free(os->segments);
+     }
+     av_freep(&c->streams);
+ }
+ static void output_segment_list(OutputStream *os, AVIOContext *out, DASHContext *c)
+ {
+     int i, start_index = 0, start_number = 1;
+     if (c->window_size) {
+         start_index  = FFMAX(os->nb_segments   - c->window_size, 0);
+         start_number = FFMAX(os->segment_index - c->window_size, 1);
+     }
+     if (c->use_template) {
+         int timescale = c->use_timeline ? os->ctx->streams[0]->time_base.den : AV_TIME_BASE;
+         avio_printf(out, "\t\t\t\t<SegmentTemplate timescale=\"%d\" ", timescale);
+         if (!c->use_timeline)
+             avio_printf(out, "duration=\"%d\" ", c->last_duration);
+         avio_printf(out, "initialization=\"init-stream$RepresentationID$.m4s\" media=\"chunk-stream$RepresentationID$-$Number%%05d$.m4s\" startNumber=\"%d\">\n", c->use_timeline ? start_number : 1);
+         if (c->use_timeline) {
+             avio_printf(out, "\t\t\t\t\t<SegmentTimeline>\n");
+             for (i = start_index; i < os->nb_segments; ) {
+                 Segment *seg = os->segments[i];
+                 int repeat = 0;
+                 avio_printf(out, "\t\t\t\t\t\t<S ");
+                 if (i == start_index)
+                     avio_printf(out, "t=\"%"PRId64"\" ", seg->time);
+                 avio_printf(out, "d=\"%d\" ", seg->duration);
+                 while (i + repeat + 1 < os->nb_segments && os->segments[i + repeat + 1]->duration == seg->duration)
+                     repeat++;
+                 if (repeat > 0)
+                     avio_printf(out, "r=\"%d\" ", repeat);
+                 avio_printf(out, "/>\n");
+                 i += 1 + repeat;
+             }
+             avio_printf(out, "\t\t\t\t\t</SegmentTimeline>\n");
+         }
+         avio_printf(out, "\t\t\t\t</SegmentTemplate>\n");
+     } else if (c->single_file) {
+         avio_printf(out, "\t\t\t\t<BaseURL>%s</BaseURL>\n", os->initfile);
+         avio_printf(out, "\t\t\t\t<SegmentList timescale=\"%d\" duration=\"%d\" startNumber=\"%d\">\n", AV_TIME_BASE, c->last_duration, start_number);
+         avio_printf(out, "\t\t\t\t\t<Initialization range=\"%"PRId64"-%"PRId64"\" />\n", os->init_start_pos, os->init_start_pos + os->init_range_length - 1);
+         for (i = start_index; i < os->nb_segments; i++) {
+             Segment *seg = os->segments[i];
+             avio_printf(out, "\t\t\t\t\t<SegmentURL mediaRange=\"%"PRId64"-%"PRId64"\" ", seg->start_pos, seg->start_pos + seg->range_length - 1);
+             if (seg->index_length)
+                 avio_printf(out, "indexRange=\"%"PRId64"-%"PRId64"\" ", seg->start_pos, seg->start_pos + seg->index_length - 1);
+             avio_printf(out, "/>\n");
+         }
+         avio_printf(out, "\t\t\t\t</SegmentList>\n");
+     } else {
+         avio_printf(out, "\t\t\t\t<SegmentList timescale=\"%d\" duration=\"%d\" startNumber=\"%d\">\n", AV_TIME_BASE, c->last_duration, start_number);
+         avio_printf(out, "\t\t\t\t\t<Initialization sourceURL=\"%s\" />\n", os->initfile);
+         for (i = start_index; i < os->nb_segments; i++) {
+             Segment *seg = os->segments[i];
+             avio_printf(out, "\t\t\t\t\t<SegmentURL media=\"%s\" />\n", seg->file);
+         }
+         avio_printf(out, "\t\t\t\t</SegmentList>\n");
+     }
+ }
+ static char *xmlescape(const char *str) {
+     int outlen = strlen(str)*3/2 + 6;
+     char *out = av_realloc(NULL, outlen + 1);
+     int pos = 0;
+     if (!out)
+         return NULL;
+     for (; *str; str++) {
+         if (pos + 6 > outlen) {
+             char *tmp;
+             outlen = 2 * outlen + 6;
+             tmp = av_realloc(out, outlen + 1);
+             if (!tmp) {
+                 av_free(out);
+                 return NULL;
+             }
+             out = tmp;
+         }
+         if (*str == '&') {
+             memcpy(&out[pos], "&amp;", 5);
+             pos += 5;
+         } else if (*str == '<') {
+             memcpy(&out[pos], "&lt;", 4);
+             pos += 4;
+         } else if (*str == '>') {
+             memcpy(&out[pos], "&gt;", 4);
+             pos += 4;
+         } else if (*str == '\'') {
+             memcpy(&out[pos], "&apos;", 6);
+             pos += 6;
+         } else if (*str == '\"') {
+             memcpy(&out[pos], "&quot;", 6);
+             pos += 6;
+         } else {
+             out[pos++] = *str;
+         }
+     }
+     out[pos] = '\0';
+     return out;
+ }
+ static void write_time(AVIOContext *out, int64_t time)
+ {
+     int seconds = time / AV_TIME_BASE;
+     int fractions = time % AV_TIME_BASE;
+     int minutes = seconds / 60;
+     int hours = minutes / 60;
+     seconds %= 60;
+     minutes %= 60;
+     avio_printf(out, "PT");
+     if (hours)
+         avio_printf(out, "%dH", hours);
+     if (hours || minutes)
+         avio_printf(out, "%dM", minutes);
+     avio_printf(out, "%d.%dS", seconds, fractions / (AV_TIME_BASE / 10));
+ }
+ static int write_manifest(AVFormatContext *s, int final)
+ {
+     DASHContext *c = s->priv_data;
+     AVIOContext *out;
+     char temp_filename[1024];
+     int ret, i;
+     AVDictionaryEntry *title = av_dict_get(s->metadata, "title", NULL, 0);
+     snprintf(temp_filename, sizeof(temp_filename), "%s.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", temp_filename);
+         return ret;
+     }
+     avio_printf(out, "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
+     avio_printf(out, "<MPD xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n"
+                 "\txmlns=\"urn:mpeg:dash:schema:mpd:2011\"\n"
+                 "\txmlns:xlink=\"http://www.w3.org/1999/xlink\"\n"
+                 "\txsi:schemaLocation=\"urn:mpeg:DASH:schema:MPD:2011 http://standards.iso.org/ittf/PubliclyAvailableStandards/MPEG-DASH_schema_files/DASH-MPD.xsd\"\n"
+                 "\tprofiles=\"urn:mpeg:dash:profile:isoff-live:2011\"\n"
+                 "\ttype=\"%s\"\n", final ? "static" : "dynamic");
+     if (final) {
+         avio_printf(out, "\tmediaPresentationDuration=\"");
+         write_time(out, c->total_duration);
+         avio_printf(out, "\"\n");
+     } else {
+         int update_period = c->last_duration / AV_TIME_BASE;
+         if (c->use_template && !c->use_timeline)
+             update_period = 500;
+         avio_printf(out, "\tminimumUpdatePeriod=\"PT%dS\"\n", update_period);
+         avio_printf(out, "\tsuggestedPresentationDelay=\"PT%dS\"\n", c->last_duration / AV_TIME_BASE);
+         if (!c->availability_start_time[0] && s->nb_streams > 0 && c->streams[0].nb_segments > 0) {
+             time_t t = time(NULL);
+             struct tm *ptm, tmbuf;
+             ptm = gmtime_r(&t, &tmbuf);
+             if (ptm) {
+                 if (!strftime(c->availability_start_time, sizeof(c->availability_start_time),
+                               "%Y-%m-%dT%H:%M:%S", ptm))
+                     c->availability_start_time[0] = '\0';
+             }
+         }
+         if (c->availability_start_time[0])
+             avio_printf(out, "\tavailabilityStartTime=\"%s\"\n", c->availability_start_time);
+         if (c->window_size && c->use_template) {
+             avio_printf(out, "\ttimeShiftBufferDepth=\"");
+             write_time(out, c->last_duration * c->window_size);
+             avio_printf(out, "\"\n");
+         }
+     }
+     avio_printf(out, "\tminBufferTime=\"");
+     write_time(out, c->last_duration);
+     avio_printf(out, "\">\n");
+     avio_printf(out, "\t<ProgramInformation>\n");
+     if (title) {
+         char *escaped = xmlescape(title->value);
+         avio_printf(out, "\t\t<Title>%s</Title>\n", escaped);
+         av_free(escaped);
+     }
+     avio_printf(out, "\t</ProgramInformation>\n");
+     if (c->window_size && s->nb_streams > 0 && c->streams[0].nb_segments > 0 && !c->use_template) {
+         OutputStream *os = &c->streams[0];
+         int start_index = FFMAX(os->nb_segments - c->window_size, 0);
+         int64_t start_time = av_rescale_q(os->segments[start_index]->time, s->streams[0]->time_base, AV_TIME_BASE_Q);
+         avio_printf(out, "\t<Period start=\"");
+         write_time(out, start_time);
+         avio_printf(out, "\">\n");
+     } else {
+         avio_printf(out, "\t<Period start=\"PT0.0S\">\n");
+     }
+     if (c->has_video) {
+         avio_printf(out, "\t\t<AdaptationSet id=\"video\" segmentAlignment=\"true\" bitstreamSwitching=\"true\">\n");
+         for (i = 0; i < s->nb_streams; i++) {
+             AVStream *st = s->streams[i];
+             OutputStream *os = &c->streams[i];
+             if (s->streams[i]->codec->codec_type != AVMEDIA_TYPE_VIDEO)
+                 continue;
+             avio_printf(out, "\t\t\t<Representation id=\"%d\" mimeType=\"video/mp4\" codecs=\"%s\" bandwidth=\"%d\" width=\"%d\" height=\"%d\">\n", i, os->codec_str, st->codec->bit_rate, st->codec->width, st->codec->height);
+             output_segment_list(&c->streams[i], out, c);
+             avio_printf(out, "\t\t\t</Representation>\n");
+         }
+         avio_printf(out, "\t\t</AdaptationSet>\n");
+     }
+     if (c->has_audio) {
+         avio_printf(out, "\t\t<AdaptationSet id=\"audio\" segmentAlignment=\"true\" bitstreamSwitching=\"true\">\n");
+         for (i = 0; i < s->nb_streams; i++) {
+             AVStream *st = s->streams[i];
+             OutputStream *os = &c->streams[i];
+             if (s->streams[i]->codec->codec_type != AVMEDIA_TYPE_AUDIO)
+                 continue;
+             avio_printf(out, "\t\t\t<Representation id=\"%d\" mimeType=\"audio/mp4\" codecs=\"%s\" bandwidth=\"%d\" audioSamplingRate=\"%d\">\n", i, os->codec_str, st->codec->bit_rate, st->codec->sample_rate);
+             avio_printf(out, "\t\t\t\t<AudioChannelConfiguration schemeIdUri=\"urn:mpeg:dash:23003:3:audio_channel_configuration:2011\" value=\"%d\" />\n", st->codec->channels);
+             output_segment_list(&c->streams[i], out, c);
+             avio_printf(out, "\t\t\t</Representation>\n");
+         }
+         avio_printf(out, "\t\t</AdaptationSet>\n");
+     }
+     avio_printf(out, "\t</Period>\n");
+     avio_printf(out, "</MPD>\n");
+     avio_flush(out);
+     avio_close(out);
 -            ret = ff_rename(temp_path, full_path);
++    return ff_rename(temp_filename, s->filename, s);
+ }
+ static int dash_write_header(AVFormatContext *s)
+ {
+     DASHContext *c = s->priv_data;
+     int ret = 0, i;
+     AVOutputFormat *oformat;
+     char *ptr;
+     char basename[1024];
+     if (c->single_file)
+         c->use_template = 0;
+     av_strlcpy(c->dirname, s->filename, sizeof(c->dirname));
+     ptr = strrchr(c->dirname, '/');
+     if (ptr) {
+         av_strlcpy(basename, &ptr[1], sizeof(basename));
+         ptr[1] = '\0';
+     } else {
+         c->dirname[0] = '\0';
+         av_strlcpy(basename, s->filename, sizeof(basename));
+     }
+     ptr = strrchr(basename, '.');
+     if (ptr)
+         *ptr = '\0';
+     oformat = av_guess_format("mp4", 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[i];
+         AVFormatContext *ctx;
+         AVStream *st;
+         AVDictionary *opts = NULL;
+         char filename[1024];
+         if (!s->streams[i]->codec->bit_rate) {
+             av_log(s, AV_LOG_ERROR, "No bit rate set for stream %d\n", i);
+             ret = AVERROR(EINVAL);
+             goto fail;
+         }
+         ctx = avformat_alloc_context();
+         if (!ctx) {
+             ret = AVERROR(ENOMEM);
+             goto fail;
+         }
+         os->ctx = ctx;
+         ctx->oformat = oformat;
+         ctx->interrupt_callback = s->interrupt_callback;
+         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;
+         st->time_base = s->streams[i]->time_base;
+         ctx->avoid_negative_ts = s->avoid_negative_ts;
+         ctx->pb = avio_alloc_context(os->iobuf, sizeof(os->iobuf), AVIO_FLAG_WRITE, os, NULL, dash_write, NULL);
+         if (!ctx->pb) {
+             ret = AVERROR(ENOMEM);
+             goto fail;
+         }
+         if (c->single_file)
+             snprintf(os->initfile, sizeof(os->initfile), "%s-stream%d.m4s", basename, i);
+         else
+             snprintf(os->initfile, sizeof(os->initfile), "init-stream%d.m4s", i);
+         snprintf(filename, sizeof(filename), "%s%s", c->dirname, os->initfile);
+         ret = ffurl_open(&os->out, filename, AVIO_FLAG_WRITE, &s->interrupt_callback, NULL);
+         if (ret < 0)
+             goto fail;
+         os->init_start_pos = 0;
+         av_dict_set(&opts, "movflags", "frag_custom+dash", 0);
+         if ((ret = avformat_write_header(ctx, &opts)) < 0) {
+              goto fail;
+         }
+         os->ctx_inited = 1;
+         avio_flush(ctx->pb);
+         av_dict_free(&opts);
+         if (c->single_file) {
+             os->init_range_length = avio_tell(ctx->pb);
+         } else {
+             ffurl_close(os->out);
+             os->out = NULL;
+         }
+         s->streams[i]->time_base = st->time_base;
+         // If the muxer wants to shift timestamps, request to have them shifted
+         // already before being handed to this muxer, so we don't have mismatches
+         // between the MPD and the actual segments.
+         s->avoid_negative_ts = ctx->avoid_negative_ts;
+         if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO)
+             c->has_video = 1;
+         else if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO)
+             c->has_audio = 1;
+         set_codec_str(s, os->ctx->streams[0]->codec, os->codec_str, sizeof(os->codec_str));
+         os->first_dts = AV_NOPTS_VALUE;
+         os->segment_index = 1;
+     }
+     if (!c->has_video && c->min_seg_duration <= 0) {
+         av_log(s, AV_LOG_WARNING, "no video stream and no min seg duration set\n");
+         ret = AVERROR(EINVAL);
+     }
+     ret = write_manifest(s, 0);
+ fail:
+     if (ret)
+         dash_free(s);
+     return ret;
+ }
+ static int add_segment(OutputStream *os, const char *file,
+                        int64_t time, int duration,
+                        int64_t start_pos, int64_t range_length,
+                        int64_t index_length)
+ {
+     int err;
+     Segment *seg;
+     if (os->nb_segments >= os->segments_size) {
+         os->segments_size = (os->segments_size + 1) * 2;
+         if ((err = av_reallocp(&os->segments, sizeof(*os->segments) *
+                                os->segments_size)) < 0) {
+             os->segments_size = 0;
+             os->nb_segments = 0;
+             return err;
+         }
+     }
+     seg = av_mallocz(sizeof(*seg));
+     if (!seg)
+         return AVERROR(ENOMEM);
+     av_strlcpy(seg->file, file, sizeof(seg->file));
+     seg->time = time;
+     seg->duration = duration;
+     seg->start_pos = start_pos;
+     seg->range_length = range_length;
+     seg->index_length = index_length;
+     os->segments[os->nb_segments++] = seg;
+     os->segment_index++;
+     return 0;
+ }
+ static void write_styp(AVIOContext *pb)
+ {
+     avio_wb32(pb, 24);
+     ffio_wfourcc(pb, "styp");
+     ffio_wfourcc(pb, "msdh");
+     avio_wb32(pb, 0); /* minor */
+     ffio_wfourcc(pb, "msdh");
+     ffio_wfourcc(pb, "msix");
+ }
+ static void find_index_range(AVFormatContext *s, const char *dirname,
+                              const char *filename, int64_t pos,
+                              int *index_length)
+ {
+     char full_path[1024];
+     uint8_t buf[8];
+     URLContext *fd;
+     int ret;
+     snprintf(full_path, sizeof(full_path), "%s%s", dirname, filename);
+     ret = ffurl_open(&fd, full_path, AVIO_FLAG_READ, &s->interrupt_callback, NULL);
+     if (ret < 0)
+         return;
+     if (ffurl_seek(fd, pos, SEEK_SET) != pos) {
+         ffurl_close(fd);
+         return;
+     }
+     ret = ffurl_read(fd, buf, 8);
+     ffurl_close(fd);
+     if (ret < 8)
+         return;
+     if (AV_RL32(&buf[4]) != MKTAG('s', 'i', 'd', 'x'))
+         return;
+     *index_length = AV_RB32(&buf[0]);
+ }
+ static int dash_flush(AVFormatContext *s, int final)
+ {
+     DASHContext *c = s->priv_data;
+     int i, ret = 0;
+     for (i = 0; i < s->nb_streams; i++) {
+         OutputStream *os = &c->streams[i];
+         char filename[1024] = "", full_path[1024], temp_path[1024];
+         int64_t start_pos = avio_tell(os->ctx->pb);
+         int range_length, index_length = 0;
+         if (!os->packets_written)
+             continue;
+         if (!c->single_file) {
+             snprintf(filename, sizeof(filename), "chunk-stream%d-%05d.m4s", i, os->segment_index);
+             snprintf(full_path, sizeof(full_path), "%s%s", c->dirname, filename);
+             snprintf(temp_path, sizeof(temp_path), "%s.tmp", full_path);
+             ret = ffurl_open(&os->out, temp_path, AVIO_FLAG_WRITE, &s->interrupt_callback, NULL);
+             if (ret < 0)
+                 break;
+             write_styp(os->ctx->pb);
+         }
+         av_write_frame(os->ctx, NULL);
+         avio_flush(os->ctx->pb);
+         os->packets_written = 0;
+         range_length = avio_tell(os->ctx->pb) - start_pos;
+         if (c->single_file) {
+             find_index_range(s, c->dirname, os->initfile, start_pos, &index_length);
+         } else {
+             ffurl_close(os->out);
+             os->out = NULL;
 -    return ff_write_chained(os->ctx, 0, pkt, s);
++            ret = ff_rename(temp_path, full_path, s);
+             if (ret < 0)
+                 break;
+         }
+         add_segment(os, filename, os->start_dts, os->end_dts - os->start_dts, start_pos, range_length, index_length);
+     }
+     if (c->window_size || (final && c->remove_at_exit)) {
+         for (i = 0; i < s->nb_streams; i++) {
+             OutputStream *os = &c->streams[i];
+             int j;
+             int remove = os->nb_segments - c->window_size - c->extra_window_size;
+             if (final && c->remove_at_exit)
+                 remove = os->nb_segments;
+             if (remove > 0) {
+                 for (j = 0; j < remove; j++) {
+                     char filename[1024];
+                     snprintf(filename, sizeof(filename), "%s%s", c->dirname, os->segments[j]->file);
+                     unlink(filename);
+                     av_free(os->segments[j]);
+                 }
+                 os->nb_segments -= remove;
+                 memmove(os->segments, os->segments + remove, os->nb_segments * sizeof(*os->segments));
+             }
+         }
+     }
+     if (ret >= 0)
+         ret = write_manifest(s, final);
+     return ret;
+ }
+ static int dash_write_packet(AVFormatContext *s, AVPacket *pkt)
+ {
+     DASHContext *c = s->priv_data;
+     AVStream *st = s->streams[pkt->stream_index];
+     OutputStream *os = &c->streams[pkt->stream_index];
+     int64_t seg_end_duration = (c->nb_segments + 1) * (int64_t) c->min_seg_duration;
+     int ret;
+     // If forcing the stream to start at 0, the mp4 muxer will set the start
+     // timestamps to 0. Do the same here, to avoid mismatches in duration/timestamps.
+     if (os->first_dts == AV_NOPTS_VALUE &&
+         s->avoid_negative_ts == AVFMT_AVOID_NEG_TS_MAKE_ZERO) {
+         pkt->pts -= pkt->dts;
+         pkt->dts  = 0;
+     }
+     if (os->first_dts == AV_NOPTS_VALUE)
+         os->first_dts = pkt->dts;
+     if ((!c->has_video || st->codec->codec_type == AVMEDIA_TYPE_VIDEO) &&
+         pkt->flags & AV_PKT_FLAG_KEY && os->packets_written &&
+         av_compare_ts(pkt->dts - os->first_dts, st->time_base,
+                       seg_end_duration, AV_TIME_BASE_Q) >= 0) {
+         int64_t prev_duration = c->last_duration;
+         c->last_duration = av_rescale_q(pkt->dts - os->start_dts,
+                                         st->time_base,
+                                         AV_TIME_BASE_Q);
+         c->total_duration = av_rescale_q(pkt->dts - os->first_dts,
+                                          st->time_base,
+                                          AV_TIME_BASE_Q);
+         if ((!c->use_timeline || !c->use_template) && prev_duration) {
+             if (c->last_duration < prev_duration*9/10 ||
+                 c->last_duration > prev_duration*11/10) {
+                 av_log(s, AV_LOG_WARNING,
+                        "Segment durations differ too much, enable use_timeline "
+                        "and use_template, or keep a stricter keyframe interval\n");
+             }
+         }
+         if ((ret = dash_flush(s, 0)) < 0)
+             return ret;
+         c->nb_segments++;
+     }
+     if (!os->packets_written)
+         os->start_dts = pkt->dts;
+     os->end_dts = pkt->dts + pkt->duration;
+     os->packets_written++;
++    return ff_write_chained(os->ctx, 0, pkt, s, 0);
+ }
+ static int dash_write_trailer(AVFormatContext *s)
+ {
+     DASHContext *c = s->priv_data;
+     if (s->nb_streams > 0) {
+         OutputStream *os = &c->streams[0];
+         // If no segments have been written so far, try to do a crude
+         // guess of the segment duration
+         if (!c->last_duration)
+             c->last_duration = av_rescale_q(os->end_dts - os->start_dts,
+                                             s->streams[0]->time_base,
+                                             AV_TIME_BASE_Q);
+         c->total_duration = av_rescale_q(os->end_dts - os->first_dts,
+                                          s->streams[0]->time_base,
+                                          AV_TIME_BASE_Q);
+     }
+     dash_flush(s, 1);
+     if (c->remove_at_exit) {
+         char filename[1024];
+         int i;
+         for (i = 0; i < s->nb_streams; i++) {
+             OutputStream *os = &c->streams[i];
+             snprintf(filename, sizeof(filename), "%s%s", c->dirname, os->initfile);
+             unlink(filename);
+         }
+         unlink(s->filename);
+     }
+     dash_free(s);
+     return 0;
+ }
+ #define OFFSET(x) offsetof(DASHContext, x)
+ #define E AV_OPT_FLAG_ENCODING_PARAM
+ static const AVOption options[] = {
+     { "window_size", "number of segments kept in the manifest", OFFSET(window_size), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, E },
+     { "extra_window_size", "number of segments kept outside of the manifest before removing from disk", OFFSET(extra_window_size), AV_OPT_TYPE_INT, { .i64 = 5 }, 0, INT_MAX, E },
+     { "min_seg_duration", "minimum segment duration (in microseconds)", OFFSET(min_seg_duration), AV_OPT_TYPE_INT64, { .i64 = 5000000 }, 0, INT_MAX, E },
+     { "remove_at_exit", "remove all segments when finished", OFFSET(remove_at_exit), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, E },
+     { "use_template", "Use SegmentTemplate instead of SegmentList", OFFSET(use_template), AV_OPT_TYPE_INT, { .i64 = 1 }, 0, 1, E },
+     { "use_timeline", "Use SegmentTimeline in SegmentTemplate", OFFSET(use_timeline), AV_OPT_TYPE_INT, { .i64 = 1 }, 0, 1, E },
+     { "single_file", "Store all segments in one file, accessed using byte ranges", OFFSET(single_file), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, E },
+     { NULL },
+ };
+ static const AVClass dash_class = {
+     .class_name = "dash muxer",
+     .item_name  = av_default_item_name,
+     .option     = options,
+     .version    = LIBAVUTIL_VERSION_INT,
+ };
+ AVOutputFormat ff_dash_muxer = {
+     .name           = "dash",
+     .long_name      = NULL_IF_CONFIG_SMALL("DASH Muxer"),
+     .priv_data_size = sizeof(DASHContext),
+     .audio_codec    = AV_CODEC_ID_AAC,
+     .video_codec    = AV_CODEC_ID_H264,
+     .flags          = AVFMT_GLOBALHEADER | AVFMT_NOFILE | AVFMT_TS_NEGATIVE,
+     .write_header   = dash_write_header,
+     .write_packet   = dash_write_packet,
+     .write_trailer  = dash_write_trailer,
+     .codec_tag      = (const AVCodecTag* const []){ ff_mp4_obj_type, 0 },
+     .priv_class     = &dash_class,
+ };
@@@ -30,8 -30,8 +30,8 @@@
  #include "libavutil/version.h"
  
  #define LIBAVFORMAT_VERSION_MAJOR 56
- #define LIBAVFORMAT_VERSION_MINOR  13
- #define LIBAVFORMAT_VERSION_MICRO 101
 -#define LIBAVFORMAT_VERSION_MINOR  7
 -#define LIBAVFORMAT_VERSION_MICRO  0
++#define LIBAVFORMAT_VERSION_MINOR  14
++#define LIBAVFORMAT_VERSION_MICRO 100
  
  #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \
                                                 LIBAVFORMAT_VERSION_MINOR, \