diff --git a/doc/muxers.texi b/doc/muxers.texi
index 0d856db..9e8826e 100644
--- a/doc/muxers.texi
+++ b/doc/muxers.texi
@@ -129,6 +129,68 @@ and the input video converted to MPEG-2 video, use the command:
 ffmpeg -i INPUT -c:a pcm_u8 -c:v mpeg2video -f crc -
 @end example
 
+@anchor{dash}
+@section dash
+
+Dynamic Adaptive Streaming over HTTP (DASH) muxer that creates segments
+and manifest files according to the MPEG-DASH standard ISO/IEC 23009-1:2014.
+
+For more information see:
+
+@itemize @bullet
+@item
+ISO DASH Specification: @url{http://standards.iso.org/ittf/PubliclyAvailableStandards/c065274_ISO_IEC_23009-1_2014.zip}
+@item
+WebM DASH Specification: @url{https://sites.google.com/a/webmproject.org/wiki/adaptive-streaming/webm-dash-specification}
+@end itemize
+
+It creates a MPD manifest file and segment files for each stream.
+
+The segment filename might contain pre-defined identifiers used with SegmentTemplate
+as defined in section 5.3.9.4.4 of the standard. Available identifiers are "$RepresentationID$",
+"$Number$", "$Bandwidth$" and "$Time$".
+
+@example
+ffmpeg -re -i <input> -map 0 -map 0 -c:a libfdk_aac -c:v libx264
+-b:v:0 800k -b:v:1 300k -s:v:1 320x170 -profile:v:1 baseline
+-profile:v:0 main -bf 1 -keyint_min 120 -g 120 -sc_threshold 0
+-b_strategy 0 -ar:a:1 22050 -use_timeline 1 -use_template 1
+-window_size 5 -adaptation_sets "id=0,streams=v id=1,streams=a"
+-f dash /path/to/out.mpd
+@end example
+
+@table @option
+@item -min_seg_duration @var{microseconds}
+Set the segment length in microseconds.
+@item -window_size @var{size}
+Set the maximum number of segments kept in the manifest.
+@item -extra_window_size @var{size}
+Set the maximum number of segments kept outside of the manifest before removing from disk.
+@item -remove_at_exit @var{remove}
+Enable (1) or disable (0) removal of all segments when finished.
+@item -use_template @var{template}
+Enable (1) or disable (0) use of SegmentTemplate instead of SegmentList.
+@item -use_timeline @var{timeline}
+Enable (1) or disable (0) use of SegmentTimeline in SegmentTemplate.
+@item -single_file @var{single_file}
+Enable (1) or disable (0) storing all segments in one file, accessed using byte ranges.
+@item -single_file_name @var{file_name}
+DASH-templated name to be used for baseURL. Implies @var{single_file} set to "1".
+@item -init_seg_name @var{init_name}
+DASH-templated name to used for the initialization segment. Default is "init-stream$RepresentationID$.m4s"
+@item -media_seg_name @var{segment_name}
+DASH-templated name to used for the media segments. Default is "chunk-stream$RepresentationID$-$Number%05d$.m4s"
+@item -utc_timing_url @var{utc_url}
+URL of the page that will return the UTC timestamp in ISO format. Example: "https://time.akamai.com/?iso"
+@item -adaptation_sets @var{adaptation_sets}
+Assign streams to AdaptationSets. Syntax is "id=x,streams=a,b,c id=y,streams=d,e" with x and y being the IDs
+of the adaptation sets and a,b,c,d and e are the indices of the mapped streams.
+
+To map all video (or audio) streams to an AdaptationSet, "v" (or "a") can be used as stream identifier instead of IDs.
+
+When no assignment is defined, this defaults to an AdaptationSet for each stream.
+@end table
+
 @section flv
 
 Adobe Flash Video Format muxer.
diff --git a/libavformat/dashenc.c b/libavformat/dashenc.c
index 534fa75..2dec117 100644
--- a/libavformat/dashenc.c
+++ b/libavformat/dashenc.c
@@ -25,6 +25,7 @@
 #endif
 
 #include "libavutil/avassert.h"
+#include "libavutil/avutil.h"
 #include "libavutil/avstring.h"
 #include "libavutil/intreadwrite.h"
 #include "libavutil/mathematics.h"
@@ -59,14 +60,22 @@ typedef struct Segment {
     int n;
 } Segment;
 
+typedef struct AdaptationSet {
+    char id[10];
+    enum AVMediaType media_type;
+    AVRational min_frame_rate, max_frame_rate;
+    int ambiguous_frame_rate;
+    AVDictionary *metadata;
+} AdaptationSet;
+
 typedef struct OutputStream {
     AVFormatContext *ctx;
-    int ctx_inited;
-    uint8_t iobuf[32768];
+    int ctx_inited, as_idx;
     AVIOContext *out;
+    char format_name[8];
     int packets_written;
     char initfile[1024];
-    int64_t init_start_pos;
+    int64_t init_start_pos, pos;
     int init_range_length;
     int nb_segments, segments_size, segment_index;
     Segment **segments;
@@ -80,6 +89,9 @@ typedef struct OutputStream {
 
 typedef struct DASHContext {
     const AVClass *class;  /* Class for private options. */
+    char *adaptation_sets;
+    AdaptationSet *as;
+    int nb_as;
     int window_size;
     int extra_window_size;
     int min_seg_duration;
@@ -88,7 +100,7 @@ typedef struct DASHContext {
     int use_timeline;
     int single_file;
     OutputStream *streams;
-    int has_video, has_audio;
+    int has_video;
     int64_t last_duration;
     int64_t total_duration;
     char availability_start_time[100];
@@ -96,24 +108,35 @@ typedef struct DASHContext {
     const char *single_file_name;
     const char *init_seg_name;
     const char *media_seg_name;
-    AVRational min_frame_rate, max_frame_rate;
-    int ambiguous_frame_rate;
+    const char *utc_timing_url;
 } DASHContext;
 
-static int dash_write(void *opaque, uint8_t *buf, int buf_size)
-{
-    OutputStream *os = opaque;
-    if (os->out)
-        avio_write(os->out, buf, buf_size);
-    return buf_size;
-}
+static struct codec_string {
+    int id;
+    const char *str;
+} codecs[] = {
+    { AV_CODEC_ID_VP8, "vp8" },
+    { AV_CODEC_ID_VP9, "vp9" },
+    { AV_CODEC_ID_VORBIS, "vorbis" },
+    { AV_CODEC_ID_OPUS, "opus" },
+    { 0, NULL }
+};
 
-// RFC 6381
 static void set_codec_str(AVFormatContext *s, AVCodecParameters *par,
                           char *str, int size)
 {
     const AVCodecTag *tags[2] = { NULL, NULL };
     uint32_t tag;
+    int i;
+
+    // common Webm codecs are not part of RFC 6381
+    for (i = 0; codecs[i].id; i++)
+        if (codecs[i].id == par->codec_id) {
+            av_strlcpy(str, codecs[i].str, size);
+            return;
+        }
+
+    // for codecs part of RFC 6381
     if (par->codec_type == AVMEDIA_TYPE_VIDEO)
         tags[0] = ff_codec_movvideo_tags;
     else if (par->codec_type == AVMEDIA_TYPE_AUDIO)
@@ -174,10 +197,55 @@ static void set_codec_str(AVFormatContext *s, AVCodecParameters *par,
     }
 }
 
+static int flush_dynbuf(OutputStream *os, int *range_length)
+{
+    uint8_t *buffer;
+
+    if (!os->ctx->pb) {
+        return AVERROR(EINVAL);
+    }
+
+    // flush
+    av_write_frame(os->ctx, NULL);
+    avio_flush(os->ctx->pb);
+
+    // write out to file
+    *range_length = avio_close_dyn_buf(os->ctx->pb, &buffer);
+    os->ctx->pb = NULL;
+    avio_write(os->out, buffer, *range_length);
+    av_free(buffer);
+
+    // re-open buffer
+    return avio_open_dyn_buf(&os->ctx->pb);
+}
+
+static int flush_init_segment(AVFormatContext *s, OutputStream *os)
+{
+    DASHContext *c = s->priv_data;
+    int ret, range_length;
+
+    ret = flush_dynbuf(os, &range_length);
+    if (ret < 0)
+        return ret;
+
+    os->pos = os->init_range_length = range_length;
+    if (!c->single_file)
+        ff_format_io_close(s, &os->out);
+    return 0;
+}
+
 static void dash_free(AVFormatContext *s)
 {
     DASHContext *c = s->priv_data;
     int i, j;
+
+    if (c->as) {
+        for (i = 0; i < c->nb_as; i++)
+            av_dict_free(&c->as[i].metadata);
+        av_freep(&c->as);
+        c->nb_as = 0;
+    }
+
     if (!c->streams)
         return;
     for (i = 0; i < s->nb_streams; i++) {
@@ -185,7 +253,7 @@ static void dash_free(AVFormatContext *s)
         if (os->ctx && os->ctx_inited)
             av_write_trailer(os->ctx);
         if (os->ctx && os->ctx->pb)
-            av_free(os->ctx->pb);
+            ffio_free_dyn_buf(&os->ctx->pb);
         ff_format_io_close(s, &os->out);
         if (os->ctx)
             avformat_free_context(os->ctx);
@@ -433,11 +501,191 @@ static void format_date_now(char *buf, int size)
     struct tm *ptm, tmbuf;
     ptm = gmtime_r(&t, &tmbuf);
     if (ptm) {
-        if (!strftime(buf, size, "%Y-%m-%dT%H:%M:%S", ptm))
+        if (!strftime(buf, size, "%Y-%m-%dT%H:%M:%SZ", ptm))
             buf[0] = '\0';
     }
 }
 
+static int write_adaptation_set(AVFormatContext *s, AVIOContext *out, int as_index)
+{
+    DASHContext *c = s->priv_data;
+    AdaptationSet *as = &c->as[as_index];
+    AVDictionaryEntry *lang, *role;
+    int i;
+
+    avio_printf(out, "\t\t<AdaptationSet id=\"%s\" contentType=\"%s\" segmentAlignment=\"true\" bitstreamSwitching=\"true\"",
+                as->id, as->media_type == AVMEDIA_TYPE_VIDEO ? "video" : "audio");
+    if (as->max_frame_rate.num && !as->ambiguous_frame_rate)
+            avio_printf(out, " %s=\"%d/%d\"", (av_cmp_q(as->min_frame_rate, as->max_frame_rate) < 0) ? "maxFrameRate" : "frameRate", as->max_frame_rate.num, as->max_frame_rate.den);
+    lang = av_dict_get(as->metadata, "language", NULL, 0);
+    if (lang)
+        avio_printf(out, " lang=\"%s\"", lang->value);
+    avio_printf(out, ">\n");
+
+    role = av_dict_get(as->metadata, "role", NULL, 0);
+    if (role)
+        avio_printf(out, "\t\t\t<Role schemeIdUri=\"urn:mpeg:dash:role:2011\" value=\"%s\"/>\n", role->value);
+
+    for (i = 0; i < s->nb_streams; i++) {
+        OutputStream *os = &c->streams[i];
+
+        if (os->as_idx - 1 != as_index)
+            continue;
+
+        if (as->media_type == AVMEDIA_TYPE_VIDEO) {
+            avio_printf(out, "\t\t\t<Representation id=\"%d\" mimeType=\"video/%s\" codecs=\"%s\"%s width=\"%d\" height=\"%d\">\n",
+                i, os->format_name, os->codec_str, os->bandwidth_str, s->streams[i]->codecpar->width, s->streams[i]->codecpar->height);
+            if (s->streams[i]->avg_frame_rate.num)
+                avio_printf(out, " frameRate=\"%d/%d\"", s->streams[i]->avg_frame_rate.num, s->streams[i]->avg_frame_rate.den);
+            avio_printf(out, ">\n");
+        } else {
+            avio_printf(out, "\t\t\t<Representation id=\"%d\" mimeType=\"audio/%s\" codecs=\"%s\"%s audioSamplingRate=\"%d\">\n",
+                i, os->format_name, os->codec_str, os->bandwidth_str, s->streams[i]->codecpar->sample_rate);
+            avio_printf(out, "\t\t\t\t<AudioChannelConfiguration schemeIdUri=\"urn:mpeg:dash:23003:3:audio_channel_configuration:2011\" value=\"%d\" />\n",
+                s->streams[i]->codecpar->channels);
+        }
+        output_segment_list(os, out, c);
+        avio_printf(out, "\t\t\t</Representation>\n");
+    }
+    avio_printf(out, "\t\t</AdaptationSet>\n");
+
+    return 0;
+}
+
+static int add_adaptation_set(AVFormatContext *s, AdaptationSet **as, enum AVMediaType type)
+{
+    DASHContext *c = s->priv_data;
+
+    void *mem = av_realloc(c->as, sizeof(*c->as) * (c->nb_as + 1));
+    if (!mem)
+        return AVERROR(ENOMEM);
+    c->as = mem;
+    ++c->nb_as;
+
+    *as = &c->as[c->nb_as - 1];
+    memset(*as, 0, sizeof(**as));
+    (*as)->media_type = type;
+
+    return 0;
+}
+
+static int adaptation_set_add_stream(AVFormatContext *s, int as_idx, int i)
+{
+    DASHContext *c = s->priv_data;
+    AdaptationSet *as = &c->as[as_idx - 1];
+    OutputStream *os = &c->streams[i];
+
+    if (as->media_type != s->streams[i]->codecpar->codec_type) {
+        av_log(s, AV_LOG_ERROR, "Codec type of stream %d doesn't match AdaptationSet's media type\n", i);
+        return AVERROR(EINVAL);
+    } else if (os->as_idx) {
+        av_log(s, AV_LOG_ERROR, "Stream %d is already assigned to an AdaptationSet\n", i);
+        return AVERROR(EINVAL);
+    }
+    os->as_idx = as_idx;
+
+    return 0;
+}
+
+static int parse_adaptation_sets(AVFormatContext *s)
+{
+    DASHContext *c = s->priv_data;
+    const char *p = c->adaptation_sets;
+    enum { new_set, parse_id, parsing_streams } state;
+    AdaptationSet *as;
+    int i, n, ret;
+
+    // default: one AdaptationSet for each stream
+    if (!p) {
+        for (i = 0; i < s->nb_streams; i++) {
+            if ((ret = add_adaptation_set(s, &as, s->streams[i]->codecpar->codec_type)) < 0)
+                return ret;
+            snprintf(as->id, sizeof(as->id), "%d", i);
+
+            c->streams[i].as_idx = c->nb_as;
+        }
+        goto end;
+    }
+
+    // syntax id=0,streams=0,1,2 id=1,streams=3,4 and so on
+    state = new_set;
+    while (*p) {
+        if (*p == ' ') {
+            p++;
+            continue;
+        } else if (state == new_set && av_strstart(p, "id=", &p)) {
+
+            if ((ret = add_adaptation_set(s, &as, AVMEDIA_TYPE_UNKNOWN)) < 0)
+                return ret;
+
+            n = strcspn(p, ",");
+            snprintf(as->id, sizeof(as->id), "%.*s", n, p);
+
+            p += n;
+            if (*p)
+                p++;
+            state = parse_id;
+        } else if (state == parse_id && av_strstart(p, "streams=", &p)) {
+            state = parsing_streams;
+        } else if (state == parsing_streams) {
+            AdaptationSet *as = &c->as[c->nb_as - 1];
+            char idx_str[8], *end_str;
+
+            n = strcspn(p, " ,");
+            snprintf(idx_str, sizeof(idx_str), "%.*s", n, p);
+            p += n;
+
+            // if value is "a" or "v", map all streams of that type
+            if (as->media_type == AVMEDIA_TYPE_UNKNOWN && (idx_str[0] == 'v' || idx_str[0] == 'a')) {
+                enum AVMediaType type = (idx_str[0] == 'v') ? AVMEDIA_TYPE_VIDEO : AVMEDIA_TYPE_AUDIO;
+                av_log(s, AV_LOG_DEBUG, "Map all streams of type %s\n", idx_str);
+
+                for (i = 0; i < s->nb_streams; i++) {
+                    if (s->streams[i]->codecpar->codec_type != type)
+                        continue;
+
+                    as->media_type = s->streams[i]->codecpar->codec_type;
+
+                    if ((ret = adaptation_set_add_stream(s, c->nb_as, i)) < 0)
+                        return ret;
+                }
+            } else { // select single stream
+                i = strtol(idx_str, &end_str, 10);
+                if (idx_str == end_str || i < 0 || i >= s->nb_streams) {
+                    av_log(s, AV_LOG_ERROR, "Selected stream \"%s\" not found!\n", idx_str);
+                    return AVERROR(EINVAL);
+                }
+                av_log(s, AV_LOG_DEBUG, "Map stream %d\n", i);
+
+                if (as->media_type == AVMEDIA_TYPE_UNKNOWN) {
+                    as->media_type = s->streams[i]->codecpar->codec_type;
+                }
+
+                if ((ret = adaptation_set_add_stream(s, c->nb_as, i)) < 0)
+                    return ret;
+            }
+
+            if (*p == ' ')
+                state = new_set;
+            if (*p)
+                p++;
+        } else {
+            return AVERROR(EINVAL);
+        }
+    }
+
+end:
+    // check for unassigned streams
+    for (i = 0; i < s->nb_streams; i++) {
+        OutputStream *os = &c->streams[i];
+        if (!os->as_idx) {
+            av_log(s, AV_LOG_ERROR, "Stream %d is not mapped to an AdaptationSet\n", i);
+            return AVERROR(EINVAL);
+        }
+    }
+    return 0;
+}
+
 static int write_manifest(AVFormatContext *s, int final)
 {
     DASHContext *c = s->priv_data;
@@ -485,7 +733,7 @@ static int write_manifest(AVFormatContext *s, int final)
         }
     }
     avio_printf(out, "\tminBufferTime=\"");
-    write_time(out, c->last_duration);
+    write_time(out, c->last_duration * 2);
     avio_printf(out, "\">\n");
     avio_printf(out, "\t<ProgramInformation>\n");
     if (title) {
@@ -494,55 +742,23 @@ static int write_manifest(AVFormatContext *s, int final)
         av_free(escaped);
     }
     avio_printf(out, "\t</ProgramInformation>\n");
+    if (c->utc_timing_url)
+        avio_printf(out, "\t<UTCTiming schemeIdUri=\"urn:mpeg:dash:utc:http-xsdate:2014\" value=\"%s\"/>\n", c->utc_timing_url);
+
     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=\"");
+        avio_printf(out, "\t<Period id=\"0\" start=\"");
         write_time(out, start_time);
         avio_printf(out, "\">\n");
     } else {
-        avio_printf(out, "\t<Period start=\"PT0.0S\">\n");
+        avio_printf(out, "\t<Period id=\"0\" start=\"PT0.0S\">\n");
     }
 
-    if (c->has_video) {
-        avio_printf(out, "\t\t<AdaptationSet contentType=\"video\" segmentAlignment=\"true\" bitstreamSwitching=\"true\"");
-        if (c->max_frame_rate.num && !c->ambiguous_frame_rate)
-            avio_printf(out, " %s=\"%d/%d\"", (av_cmp_q(c->min_frame_rate, c->max_frame_rate) < 0) ? "maxFrameRate" : "frameRate", c->max_frame_rate.num, c->max_frame_rate.den);
-        avio_printf(out, ">\n");
-
-        for (i = 0; i < s->nb_streams; i++) {
-            AVStream *st = s->streams[i];
-            OutputStream *os = &c->streams[i];
-
-            if (st->codecpar->codec_type != AVMEDIA_TYPE_VIDEO)
-                continue;
-
-            avio_printf(out, "\t\t\t<Representation id=\"%d\" mimeType=\"video/mp4\" codecs=\"%s\"%s width=\"%d\" height=\"%d\"", i, os->codec_str, os->bandwidth_str, st->codecpar->width, st->codecpar->height);
-            if (st->avg_frame_rate.num)
-                avio_printf(out, " frameRate=\"%d/%d\"", st->avg_frame_rate.num, st->avg_frame_rate.den);
-            avio_printf(out, ">\n");
-
-            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 contentType=\"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 (st->codecpar->codec_type != AVMEDIA_TYPE_AUDIO)
-                continue;
-
-            avio_printf(out, "\t\t\t<Representation id=\"%d\" mimeType=\"audio/mp4\" codecs=\"%s\"%s audioSamplingRate=\"%d\">\n", i, os->codec_str, os->bandwidth_str, st->codecpar->sample_rate);
-            avio_printf(out, "\t\t\t\t<AudioChannelConfiguration schemeIdUri=\"urn:mpeg:dash:23003:3:audio_channel_configuration:2011\" value=\"%d\" />\n", st->codecpar->channels);
-            output_segment_list(&c->streams[i], out, c);
-            avio_printf(out, "\t\t\t</Representation>\n");
-        }
-        avio_printf(out, "\t\t</AdaptationSet>\n");
+    for (i = 0; i < c->nb_as; i++) {
+        if ((ret = write_adaptation_set(s, out, i)) < 0)
+            return ret;
     }
     avio_printf(out, "\t</Period>\n");
     avio_printf(out, "</MPD>\n");
@@ -551,11 +767,18 @@ static int write_manifest(AVFormatContext *s, int final)
     return avpriv_io_move(temp_filename, s->filename);
 }
 
+static int dict_copy_entry(AVDictionary **dst, const AVDictionary *src, const char *key)
+{
+    AVDictionaryEntry *entry = av_dict_get(src, key, NULL, 0);
+    if (entry)
+        av_dict_set(dst, key, entry->value, AV_DICT_DONT_OVERWRITE);
+    return 0;
+}
+
 static int dash_init(AVFormatContext *s)
 {
     DASHContext *c = s->priv_data;
     int ret = 0, i;
-    AVOutputFormat *oformat;
     char *ptr;
     char basename[1024];
 
@@ -563,7 +786,6 @@ static int dash_init(AVFormatContext *s)
         c->single_file = 1;
     if (c->single_file)
         c->use_template = 0;
-    c->ambiguous_frame_rate = 0;
 
     av_strlcpy(c->dirname, s->filename, sizeof(c->dirname));
     ptr = strrchr(c->dirname, '/');
@@ -579,16 +801,16 @@ static int dash_init(AVFormatContext *s)
     if (ptr)
         *ptr = '\0';
 
-    oformat = av_guess_format("mp4", NULL, NULL);
-    if (!oformat)
-        return AVERROR_MUXER_NOT_FOUND;
-
     c->streams = av_mallocz(sizeof(*c->streams) * s->nb_streams);
     if (!c->streams)
         return AVERROR(ENOMEM);
 
+    if ((ret = parse_adaptation_sets(s)) < 0)
+        return AVERROR(EINVAL);
+
     for (i = 0; i < s->nb_streams; i++) {
         OutputStream *os = &c->streams[i];
+        AdaptationSet *as = &c->as[os->as_idx - 1];
         AVFormatContext *ctx;
         AVStream *st;
         AVDictionary *opts = NULL;
@@ -606,11 +828,30 @@ static int dash_init(AVFormatContext *s)
                 return AVERROR(EINVAL);
         }
 
+        // copy AdaptationSet language and role from stream metadata
+        dict_copy_entry(&as->metadata, s->streams[i]->metadata, "language");
+        dict_copy_entry(&as->metadata, s->streams[i]->metadata, "role");
+
         ctx = avformat_alloc_context();
         if (!ctx)
             return AVERROR(ENOMEM);
+
+        // choose muxer based on codec: webm for VP8/9 and opus, mp4 otherwise
+        // note: os->format_name is also used as part of the mimetype of the
+        //       representation, e.g. video/<format_name>
+        if (s->streams[i]->codecpar->codec_id == AV_CODEC_ID_VP8 ||
+            s->streams[i]->codecpar->codec_id == AV_CODEC_ID_VP9 ||
+            s->streams[i]->codecpar->codec_id == AV_CODEC_ID_OPUS ||
+            s->streams[i]->codecpar->codec_id == AV_CODEC_ID_VORBIS) {
+            snprintf(os->format_name, sizeof(os->format_name), "webm");
+        } else {
+            snprintf(os->format_name, sizeof(os->format_name), "mp4");
+        }
+        ctx->oformat = av_guess_format(os->format_name, NULL, NULL);
+        if (!ctx->oformat)
+            return AVERROR(AVERROR_MUXER_NOT_FOUND);
+
         os->ctx = ctx;
-        ctx->oformat = oformat;
         ctx->interrupt_callback = s->interrupt_callback;
         ctx->opaque             = s->opaque;
         ctx->io_close           = s->io_close;
@@ -624,8 +865,7 @@ static int dash_init(AVFormatContext *s)
         ctx->avoid_negative_ts = s->avoid_negative_ts;
         ctx->flags = s->flags;
 
-        ctx->pb = avio_alloc_context(os->iobuf, sizeof(os->iobuf), AVIO_FLAG_WRITE, os, NULL, dash_write, NULL);
-        if (!ctx->pb)
+        if ((ret = avio_open_dyn_buf(&ctx->pb)) < 0)
             return AVERROR(ENOMEM);
 
         if (c->single_file) {
@@ -642,9 +882,17 @@ static int dash_init(AVFormatContext *s)
             return ret;
         os->init_start_pos = 0;
 
-        av_dict_set(&opts, "movflags", "frag_custom+dash+delay_moov", 0);
-        if ((ret = avformat_init_output(ctx, &opts)) < 0)
+        if (!strcmp(os->format_name, "mp4")) {
+            av_dict_set(&opts, "movflags", "frag_custom+dash+delay_moov", 0);
+        } else {
+            av_dict_set_int(&opts, "dash", 1, 0);
+            av_dict_set_int(&opts, "cluster_time_limit", c->min_seg_duration / 1000, 0);
+            av_dict_set_int(&opts, "live", 1, 0);
+            av_dict_set_int(&opts, "dash_track_number", i + 1, 0);
+        }
+        if ((ret = avformat_init_output(ctx, &opts)) < 0) {
             return ret;
+        }
         os->ctx_inited = 1;
         avio_flush(ctx->pb);
         av_dict_free(&opts);
@@ -659,16 +907,14 @@ static int dash_init(AVFormatContext *s)
         if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
             AVRational avg_frame_rate = s->streams[i]->avg_frame_rate;
             if (avg_frame_rate.num > 0) {
-                if (av_cmp_q(avg_frame_rate, c->min_frame_rate) < 0)
-                    c->min_frame_rate = avg_frame_rate;
-                if (av_cmp_q(c->max_frame_rate, avg_frame_rate) < 0)
-                    c->max_frame_rate = avg_frame_rate;
+                if (av_cmp_q(avg_frame_rate, as->min_frame_rate) < 0)
+                    as->min_frame_rate = avg_frame_rate;
+                if (av_cmp_q(as->max_frame_rate, avg_frame_rate) < 0)
+                    as->max_frame_rate = avg_frame_rate;
             } else {
-                c->ambiguous_frame_rate = 1;
+                as->ambiguous_frame_rate = 1;
             }
             c->has_video = 1;
-        } else if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
-            c->has_audio = 1;
         }
 
         set_codec_str(s, st->codecpar, os->codec_str, sizeof(os->codec_str));
@@ -695,6 +941,13 @@ static int dash_write_header(AVFormatContext *s)
             dash_free(s);
             return ret;
         }
+
+        // Flush init segment
+        // except for mp4, since delay_moov is set and the init segment
+        // is then flushed after the first packets
+        if (strcmp(os->format_name, "mp4")) {
+            flush_init_segment(s, os);
+        }
     }
     ret = write_manifest(s, 0);
     if (!ret)
@@ -803,7 +1056,6 @@ static int dash_flush(AVFormatContext *s, int final, int stream)
     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;
         int range_length, index_length = 0;
 
         if (!os->packets_written)
@@ -822,14 +1074,9 @@ static int dash_flush(AVFormatContext *s, int final, int stream)
         }
 
         if (!os->init_range_length) {
-            av_write_frame(os->ctx, NULL);
-            os->init_range_length = avio_tell(os->ctx->pb);
-            if (!c->single_file)
-                ff_format_io_close(s, &os->out);
+            flush_init_segment(s, os);
         }
 
-        start_pos = avio_tell(os->ctx->pb);
-
         if (!c->single_file) {
             dash_fill_tmpl_params(filename, sizeof(filename), c->media_seg_name, i, os->segment_index, os->bit_rate, os->start_pts);
             snprintf(full_path, sizeof(full_path), "%s%s", c->dirname, filename);
@@ -837,26 +1084,39 @@ static int dash_flush(AVFormatContext *s, int final, int stream)
             ret = s->io_open(s, &os->out, temp_path, AVIO_FLAG_WRITE, NULL);
             if (ret < 0)
                 break;
-            write_styp(os->ctx->pb);
+            if (!strcmp(os->format_name, "mp4"))
+                write_styp(os->ctx->pb);
         } else {
             snprintf(full_path, sizeof(full_path), "%s%s", c->dirname, os->initfile);
         }
 
-        av_write_frame(os->ctx, NULL);
-        avio_flush(os->ctx->pb);
+        ret = flush_dynbuf(os, &range_length);
+        if (ret < 0)
+            break;
         os->packets_written = 0;
 
-        range_length = avio_tell(os->ctx->pb) - start_pos;
         if (c->single_file) {
-            find_index_range(s, full_path, start_pos, &index_length);
+            find_index_range(s, full_path, os->pos, &index_length);
         } else {
             ff_format_io_close(s, &os->out);
             ret = avpriv_io_move(temp_path, full_path);
             if (ret < 0)
                 break;
         }
-        add_segment(os, filename, os->start_pts, os->max_pts - os->start_pts, start_pos, range_length, index_length);
+
+        if (!os->bit_rate) {
+            // calculate average bitrate of first segment
+            int64_t bitrate = (int64_t) range_length * 8 * AV_TIME_BASE / (os->max_pts - os->start_pts);
+            if (bitrate >= 0) {
+                os->bit_rate = bitrate;
+                snprintf(os->bandwidth_str, sizeof(os->bandwidth_str),
+                     " bandwidth=\"%d\"", os->bit_rate);
+            }
+        }
+        add_segment(os, filename, os->start_pts, os->max_pts - os->start_pts, os->pos, range_length, index_length);
         av_log(s, AV_LOG_VERBOSE, "Representation %d media segment %d written to: %s\n", i, os->segment_index, full_path);
+
+        os->pos += range_length;
     }
 
     if (c->window_size || (final && c->remove_at_exit)) {
@@ -889,7 +1149,6 @@ 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 = (os->segment_index) * (int64_t) c->min_seg_duration;
     int ret;
 
     ret = update_stream_extradata(s, os, st->codecpar);
@@ -919,8 +1178,8 @@ static int dash_write_packet(AVFormatContext *s, AVPacket *pkt)
 
     if ((!c->has_video || st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) &&
         pkt->flags & AV_PKT_FLAG_KEY && os->packets_written &&
-        av_compare_ts(pkt->pts - os->first_pts, st->time_base,
-                      seg_end_duration, AV_TIME_BASE_Q) >= 0) {
+        av_compare_ts(pkt->pts - os->start_pts, st->time_base,
+                      c->min_seg_duration, AV_TIME_BASE_Q) >= 0) {
         int64_t prev_duration = c->last_duration;
 
         c->last_duration = av_rescale_q(pkt->pts - os->start_pts,
@@ -1018,6 +1277,7 @@ static int dash_check_bitstream(struct AVFormatContext *s, const AVPacket *avpkt
 #define OFFSET(x) offsetof(DASHContext, x)
 #define E AV_OPT_FLAG_ENCODING_PARAM
 static const AVOption options[] = {
+    { "adaptation_sets", "Adaptation sets. Syntax: id=0,streams=0,1,2 id=1,streams=3,4 and so on", OFFSET(adaptation_sets), AV_OPT_TYPE_STRING, { 0 }, 0, 0, AV_OPT_FLAG_ENCODING_PARAM },
     { "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 },
@@ -1028,6 +1288,7 @@ static const AVOption options[] = {
     { "single_file_name", "DASH-templated name to be used for baseURL. Implies storing all segments in one file, accessed using byte ranges", OFFSET(single_file_name), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, E },
     { "init_seg_name", "DASH-templated name to used for the initialization segment", OFFSET(init_seg_name), AV_OPT_TYPE_STRING, {.str = "init-stream$RepresentationID$.m4s"}, 0, 0, E },
     { "media_seg_name", "DASH-templated name to used for the media segments", OFFSET(media_seg_name), AV_OPT_TYPE_STRING, {.str = "chunk-stream$RepresentationID$-$Number%05d$.m4s"}, 0, 0, E },
+    { "utc_timing_url", "URL of the page that will return the UTC timestamp in ISO format", OFFSET(utc_timing_url), AV_OPT_TYPE_STRING, { 0 }, 0, 0, AV_OPT_FLAG_ENCODING_PARAM },
     { NULL },
 };
 
diff --git a/libavformat/version.h b/libavformat/version.h
index ad25596..4490f8b 100644
--- a/libavformat/version.h
+++ b/libavformat/version.h
@@ -33,7 +33,7 @@
 // Also please add any ticket numbers that you believe might be affected here
 #define LIBAVFORMAT_VERSION_MAJOR  57
 #define LIBAVFORMAT_VERSION_MINOR  56
-#define LIBAVFORMAT_VERSION_MICRO 101
+#define LIBAVFORMAT_VERSION_MICRO 102
 
 #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \
                                                LIBAVFORMAT_VERSION_MINOR, \
