2 * MPEG-DASH ISO BMFF segmenter
3 * Copyright (c) 2014 Martin Storsjo
4 * Copyright (c) 2018 Akamai Technologies, Inc.
6 * This file is part of FFmpeg.
8 * FFmpeg is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * FFmpeg is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with FFmpeg; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
28 #include "libavutil/avassert.h"
29 #include "libavutil/avutil.h"
30 #include "libavutil/avstring.h"
31 #include "libavutil/intreadwrite.h"
32 #include "libavutil/mathematics.h"
33 #include "libavutil/opt.h"
34 #include "libavutil/parseutils.h"
35 #include "libavutil/rational.h"
36 #include "libavutil/time.h"
37 #include "libavutil/time_internal.h"
42 #include "avio_internal.h"
43 #include "hlsplaylist.h"
44 #if CONFIG_HTTP_PROTOCOL
49 #include "os_support.h"
55 SEGMENT_TYPE_AUTO
= 0,
63 FRAG_TYPE_EVERY_FRAME
,
69 #define MPD_PROFILE_DASH 1
70 #define MPD_PROFILE_DVB 2
72 typedef struct Segment
{
75 int range_length
, index_length
;
77 double prog_date_time
;
82 typedef struct AdaptationSet
{
86 int64_t frag_duration
;
88 enum AVMediaType media_type
;
89 AVDictionary
*metadata
;
90 AVRational min_frame_rate
, max_frame_rate
;
91 int ambiguous_frame_rate
;
92 int64_t max_frag_duration
;
93 int max_width
, max_height
;
99 typedef struct OutputStream
{
100 AVFormatContext
*ctx
;
101 int ctx_inited
, as_idx
;
103 AVCodecParserContext
*parser
;
104 AVCodecContext
*parser_avctx
;
107 int64_t init_start_pos
, pos
;
108 int init_range_length
;
109 int nb_segments
, segments_size
, segment_index
;
110 int64_t seg_duration
;
111 int64_t frag_duration
;
112 int64_t last_duration
;
114 int64_t first_pts
, start_pts
, max_pts
;
115 int64_t last_dts
, last_pts
;
118 SegmentType segment_type
; /* segment type selected for this particular stream */
119 const char *format_name
;
120 const char *extension_name
;
121 const char *single_file_name
; /* file names selected for this particular stream */
122 const char *init_seg_name
;
123 const char *media_seg_name
;
128 char full_path
[1024];
129 char temp_path
[1024];
130 double availability_time_offset
;
131 AVProducerReferenceTime producer_reference_time
;
132 char producer_reference_time_str
[100];
134 int64_t total_pkt_duration
;
139 int coding_dependency
;
142 typedef struct DASHContext
{
143 const AVClass
*class; /* Class for private options. */
144 char *adaptation_sets
;
148 int extra_window_size
;
149 #if FF_API_DASH_MIN_SEG_DURATION
150 int min_seg_duration
;
152 int64_t seg_duration
;
153 int64_t frag_duration
;
158 OutputStream
*streams
;
160 int64_t last_duration
;
161 int64_t total_duration
;
162 char availability_start_time
[100];
164 int64_t presentation_time_offset
;
166 const char *single_file_name
; /* file names as specified in options */
167 const char *init_seg_name
;
168 const char *media_seg_name
;
169 const char *utc_timing_url
;
171 const char *user_agent
;
172 AVDictionary
*http_opts
;
175 int master_playlist_created
;
176 AVIOContext
*mpd_out
;
177 AVIOContext
*m3u8_out
;
180 int index_correction
;
181 AVDictionary
*format_options
;
183 SegmentType segment_type_option
; /* segment type as specified in options */
184 int ignore_io_errors
;
187 int master_publish_rate
;
188 int nr_of_streams_to_flush
;
189 int nr_of_streams_flushed
;
192 int64_t max_gop_size
;
193 int64_t max_segment_duration
;
195 int64_t target_latency
;
196 int target_latency_refid
;
197 AVRational min_playback_rate
;
198 AVRational max_playback_rate
;
201 static struct codec_string
{
205 { AV_CODEC_ID_VP8
, "vp8" },
206 { AV_CODEC_ID_VP9
, "vp9" },
207 { AV_CODEC_ID_VORBIS
, "vorbis" },
208 { AV_CODEC_ID_OPUS
, "opus" },
209 { AV_CODEC_ID_FLAC
, "flac" },
213 static struct format_string
{
214 SegmentType segment_type
;
217 { SEGMENT_TYPE_AUTO
, "auto" },
218 { SEGMENT_TYPE_MP4
, "mp4" },
219 { SEGMENT_TYPE_WEBM
, "webm" },
223 static int dashenc_io_open(AVFormatContext
*s
, AVIOContext
**pb
, char *filename
,
224 AVDictionary
**options
) {
225 DASHContext
*c
= s
->priv_data
;
226 int http_base_proto
= filename
? ff_is_http_proto(filename
) : 0;
227 int err
= AVERROR_MUXER_NOT_FOUND
;
228 if (!*pb
|| !http_base_proto
|| !c
->http_persistent
) {
229 err
= s
->io_open(s
, pb
, filename
, AVIO_FLAG_WRITE
, options
);
230 #if CONFIG_HTTP_PROTOCOL
232 URLContext
*http_url_context
= ffio_geturlcontext(*pb
);
233 av_assert0(http_url_context
);
234 err
= ff_http_do_new_request(http_url_context
, filename
);
236 ff_format_io_close(s
, pb
);
242 static void dashenc_io_close(AVFormatContext
*s
, AVIOContext
**pb
, char *filename
) {
243 DASHContext
*c
= s
->priv_data
;
244 int http_base_proto
= filename
? ff_is_http_proto(filename
) : 0;
249 if (!http_base_proto
|| !c
->http_persistent
) {
250 ff_format_io_close(s
, pb
);
251 #if CONFIG_HTTP_PROTOCOL
253 URLContext
*http_url_context
= ffio_geturlcontext(*pb
);
254 av_assert0(http_url_context
);
256 ffurl_shutdown(http_url_context
, AVIO_FLAG_WRITE
);
261 static const char *get_format_str(SegmentType segment_type
) {
263 for (i
= 0; i
< SEGMENT_TYPE_NB
; i
++)
264 if (formats
[i
].segment_type
== segment_type
)
265 return formats
[i
].str
;
269 static const char *get_extension_str(SegmentType type
, int single_file
)
273 case SEGMENT_TYPE_MP4
: return single_file
? "mp4" : "m4s";
274 case SEGMENT_TYPE_WEBM
: return "webm";
275 default: return NULL
;
279 static int handle_io_open_error(AVFormatContext
*s
, int err
, char *url
) {
280 DASHContext
*c
= s
->priv_data
;
281 char errbuf
[AV_ERROR_MAX_STRING_SIZE
];
282 av_strerror(err
, errbuf
, sizeof(errbuf
));
283 av_log(s
, c
->ignore_io_errors
? AV_LOG_WARNING
: AV_LOG_ERROR
,
284 "Unable to open %s for writing: %s\n", url
, errbuf
);
285 return c
->ignore_io_errors
? 0 : err
;
288 static inline SegmentType
select_segment_type(SegmentType segment_type
, enum AVCodecID codec_id
)
290 if (segment_type
== SEGMENT_TYPE_AUTO
) {
291 if (codec_id
== AV_CODEC_ID_OPUS
|| codec_id
== AV_CODEC_ID_VORBIS
||
292 codec_id
== AV_CODEC_ID_VP8
|| codec_id
== AV_CODEC_ID_VP9
) {
293 segment_type
= SEGMENT_TYPE_WEBM
;
295 segment_type
= SEGMENT_TYPE_MP4
;
302 static int init_segment_types(AVFormatContext
*s
)
304 DASHContext
*c
= s
->priv_data
;
305 int has_mp4_streams
= 0;
306 for (int i
= 0; i
< s
->nb_streams
; ++i
) {
307 OutputStream
*os
= &c
->streams
[i
];
308 SegmentType segment_type
= select_segment_type(
309 c
->segment_type_option
, s
->streams
[i
]->codecpar
->codec_id
);
310 os
->segment_type
= segment_type
;
311 os
->format_name
= get_format_str(segment_type
);
312 if (!os
->format_name
) {
313 av_log(s
, AV_LOG_ERROR
, "Could not select DASH segment type for stream %d\n", i
);
314 return AVERROR_MUXER_NOT_FOUND
;
316 os
->extension_name
= get_extension_str(segment_type
, c
->single_file
);
317 if (!os
->extension_name
) {
318 av_log(s
, AV_LOG_ERROR
, "Could not get extension type for stream %d\n", i
);
319 return AVERROR_MUXER_NOT_FOUND
;
322 has_mp4_streams
|= segment_type
== SEGMENT_TYPE_MP4
;
325 if (c
->hls_playlist
&& !has_mp4_streams
) {
326 av_log(s
, AV_LOG_WARNING
, "No mp4 streams, disabling HLS manifest generation\n");
333 static int check_file_extension(const char *filename
, const char *extension
) {
335 if (!filename
|| !extension
)
337 dot
= strrchr(filename
, '.');
338 if (dot
&& !strcmp(dot
+ 1, extension
))
343 static void set_vp9_codec_str(AVFormatContext
*s
, AVCodecParameters
*par
,
344 AVRational
*frame_rate
, char *str
, int size
) {
346 int ret
= ff_isom_get_vpcc_features(s
, par
, frame_rate
, &vpcc
);
348 av_strlcatf(str
, size
, "vp09.%02d.%02d.%02d",
349 vpcc
.profile
, vpcc
.level
, vpcc
.bitdepth
);
351 // Default to just vp9 in case of error while finding out profile or level
352 av_log(s
, AV_LOG_WARNING
, "Could not find VP9 profile and/or level\n");
353 av_strlcpy(str
, "vp9", size
);
358 static void set_codec_str(AVFormatContext
*s
, AVCodecParameters
*par
,
359 AVRational
*frame_rate
, char *str
, int size
)
361 const AVCodecTag
*tags
[2] = { NULL
, NULL
};
365 // common Webm codecs are not part of RFC 6381
366 for (i
= 0; codecs
[i
].id
; i
++)
367 if (codecs
[i
].id
== par
->codec_id
) {
368 if (codecs
[i
].id
== AV_CODEC_ID_VP9
) {
369 set_vp9_codec_str(s
, par
, frame_rate
, str
, size
);
371 av_strlcpy(str
, codecs
[i
].str
, size
);
376 // for codecs part of RFC 6381
377 if (par
->codec_type
== AVMEDIA_TYPE_VIDEO
)
378 tags
[0] = ff_codec_movvideo_tags
;
379 else if (par
->codec_type
== AVMEDIA_TYPE_AUDIO
)
380 tags
[0] = ff_codec_movaudio_tags
;
384 tag
= par
->codec_tag
;
386 tag
= av_codec_get_tag(tags
, par
->codec_id
);
394 if (!strcmp(str
, "mp4a") || !strcmp(str
, "mp4v")) {
396 tags
[0] = ff_mp4_obj_type
;
397 oti
= av_codec_get_tag(tags
, par
->codec_id
);
399 av_strlcatf(str
, size
, ".%02"PRIx32
, oti
);
403 if (tag
== MKTAG('m', 'p', '4', 'a')) {
404 if (par
->extradata_size
>= 2) {
405 int aot
= par
->extradata
[0] >> 3;
407 aot
= ((AV_RB16(par
->extradata
) >> 5) & 0x3f) + 32;
408 av_strlcatf(str
, size
, ".%d", aot
);
410 } else if (tag
== MKTAG('m', 'p', '4', 'v')) {
411 // Unimplemented, should output ProfileLevelIndication as a decimal number
412 av_log(s
, AV_LOG_WARNING
, "Incomplete RFC 6381 codec string for mp4v\n");
414 } else if (!strcmp(str
, "avc1")) {
415 uint8_t *tmpbuf
= NULL
;
416 uint8_t *extradata
= par
->extradata
;
417 int extradata_size
= par
->extradata_size
;
420 if (extradata
[0] != 1) {
422 if (avio_open_dyn_buf(&pb
) < 0)
424 if (ff_isom_write_avcc(pb
, extradata
, extradata_size
) < 0) {
425 ffio_free_dyn_buf(&pb
);
428 extradata_size
= avio_close_dyn_buf(pb
, &extradata
);
432 if (extradata_size
>= 4)
433 av_strlcatf(str
, size
, ".%02x%02x%02x",
434 extradata
[1], extradata
[2], extradata
[3]);
436 } else if (!strcmp(str
, "av01")) {
437 AV1SequenceParameters seq
;
438 if (!par
->extradata_size
)
440 if (ff_av1_parse_seq_header(&seq
, par
->extradata
, par
->extradata_size
) < 0)
443 av_strlcatf(str
, size
, ".%01u.%02u%s.%02u",
444 seq
.profile
, seq
.level
, seq
.tier
? "H" : "M", seq
.bitdepth
);
445 if (seq
.color_description_present_flag
)
446 av_strlcatf(str
, size
, ".%01u.%01u%01u%01u.%02u.%02u.%02u.%01u",
448 seq
.chroma_subsampling_x
, seq
.chroma_subsampling_y
, seq
.chroma_sample_position
,
449 seq
.color_primaries
, seq
.transfer_characteristics
, seq
.matrix_coefficients
,
454 static int flush_dynbuf(DASHContext
*c
, OutputStream
*os
, int *range_length
)
459 return AVERROR(EINVAL
);
463 av_write_frame(os
->ctx
, NULL
);
464 avio_flush(os
->ctx
->pb
);
466 if (!c
->single_file
) {
468 *range_length
= avio_close_dyn_buf(os
->ctx
->pb
, &buffer
);
471 avio_write(os
->out
, buffer
+ os
->written_len
, *range_length
- os
->written_len
);
476 return avio_open_dyn_buf(&os
->ctx
->pb
);
478 *range_length
= avio_tell(os
->ctx
->pb
) - os
->pos
;
483 static void set_http_options(AVDictionary
**options
, DASHContext
*c
)
486 av_dict_set(options
, "method", c
->method
, 0);
487 av_dict_copy(options
, c
->http_opts
, 0);
489 av_dict_set(options
, "user_agent", c
->user_agent
, 0);
490 if (c
->http_persistent
)
491 av_dict_set_int(options
, "multiple_requests", 1, 0);
493 av_dict_set_int(options
, "timeout", c
->timeout
, 0);
496 static void get_hls_playlist_name(char *playlist_name
, int string_size
,
497 const char *base_url
, int id
) {
499 snprintf(playlist_name
, string_size
, "%smedia_%d.m3u8", base_url
, id
);
501 snprintf(playlist_name
, string_size
, "media_%d.m3u8", id
);
504 static void get_start_index_number(OutputStream
*os
, DASHContext
*c
,
505 int *start_index
, int *start_number
) {
508 if (c
->window_size
) {
509 *start_index
= FFMAX(os
->nb_segments
- c
->window_size
, 0);
510 *start_number
= FFMAX(os
->segment_index
- c
->window_size
, 1);
514 static void write_hls_media_playlist(OutputStream
*os
, AVFormatContext
*s
,
515 int representation_id
, int final
,
516 char *prefetch_url
) {
517 DASHContext
*c
= s
->priv_data
;
518 int timescale
= os
->ctx
->streams
[0]->time_base
.den
;
519 char temp_filename_hls
[1024];
520 char filename_hls
[1024];
521 AVDictionary
*http_opts
= NULL
;
522 int target_duration
= 0;
524 const char *proto
= avio_find_protocol_name(c
->dirname
);
525 int use_rename
= proto
&& !strcmp(proto
, "file");
526 int i
, start_index
, start_number
;
527 double prog_date_time
= 0;
529 get_start_index_number(os
, c
, &start_index
, &start_number
);
531 if (!c
->hls_playlist
|| start_index
>= os
->nb_segments
||
532 os
->segment_type
!= SEGMENT_TYPE_MP4
)
535 get_hls_playlist_name(filename_hls
, sizeof(filename_hls
),
536 c
->dirname
, representation_id
);
538 snprintf(temp_filename_hls
, sizeof(temp_filename_hls
), use_rename
? "%s.tmp" : "%s", filename_hls
);
540 set_http_options(&http_opts
, c
);
541 ret
= dashenc_io_open(s
, &c
->m3u8_out
, temp_filename_hls
, &http_opts
);
542 av_dict_free(&http_opts
);
544 handle_io_open_error(s
, ret
, temp_filename_hls
);
547 for (i
= start_index
; i
< os
->nb_segments
; i
++) {
548 Segment
*seg
= os
->segments
[i
];
549 double duration
= (double) seg
->duration
/ timescale
;
550 if (target_duration
<= duration
)
551 target_duration
= lrint(duration
);
554 ff_hls_write_playlist_header(c
->m3u8_out
, 6, -1, target_duration
,
555 start_number
, PLAYLIST_TYPE_NONE
, 0);
557 ff_hls_write_init_file(c
->m3u8_out
, os
->initfile
, c
->single_file
,
558 os
->init_range_length
, os
->init_start_pos
);
560 for (i
= start_index
; i
< os
->nb_segments
; i
++) {
561 Segment
*seg
= os
->segments
[i
];
563 if (prog_date_time
== 0) {
564 if (os
->nb_segments
== 1)
565 prog_date_time
= c
->start_time_s
;
567 prog_date_time
= seg
->prog_date_time
;
569 seg
->prog_date_time
= prog_date_time
;
571 ret
= ff_hls_write_file_entry(c
->m3u8_out
, 0, c
->single_file
,
572 (double) seg
->duration
/ timescale
, 0,
573 seg
->range_length
, seg
->start_pos
, NULL
,
574 c
->single_file
? os
->initfile
: seg
->file
,
575 &prog_date_time
, 0, 0, 0);
577 av_log(os
->ctx
, AV_LOG_WARNING
, "ff_hls_write_file_entry get error\n");
582 avio_printf(c
->m3u8_out
, "#EXT-X-PREFETCH:%s\n", prefetch_url
);
585 ff_hls_write_end_list(c
->m3u8_out
);
587 dashenc_io_close(s
, &c
->m3u8_out
, temp_filename_hls
);
590 ff_rename(temp_filename_hls
, filename_hls
, os
->ctx
);
593 static int flush_init_segment(AVFormatContext
*s
, OutputStream
*os
)
595 DASHContext
*c
= s
->priv_data
;
596 int ret
, range_length
;
598 ret
= flush_dynbuf(c
, os
, &range_length
);
602 os
->pos
= os
->init_range_length
= range_length
;
603 if (!c
->single_file
) {
605 snprintf(filename
, sizeof(filename
), "%s%s", c
->dirname
, os
->initfile
);
606 dashenc_io_close(s
, &os
->out
, filename
);
611 static void dash_free(AVFormatContext
*s
)
613 DASHContext
*c
= s
->priv_data
;
617 for (i
= 0; i
< c
->nb_as
; i
++) {
618 av_dict_free(&c
->as
[i
].metadata
);
619 av_freep(&c
->as
[i
].descriptor
);
627 for (i
= 0; i
< s
->nb_streams
; i
++) {
628 OutputStream
*os
= &c
->streams
[i
];
629 if (os
->ctx
&& os
->ctx
->pb
) {
631 ffio_free_dyn_buf(&os
->ctx
->pb
);
633 avio_close(os
->ctx
->pb
);
635 ff_format_io_close(s
, &os
->out
);
636 avformat_free_context(os
->ctx
);
637 avcodec_free_context(&os
->parser_avctx
);
638 av_parser_close(os
->parser
);
639 for (j
= 0; j
< os
->nb_segments
; j
++)
640 av_free(os
->segments
[j
]);
641 av_free(os
->segments
);
642 av_freep(&os
->single_file_name
);
643 av_freep(&os
->init_seg_name
);
644 av_freep(&os
->media_seg_name
);
646 av_freep(&c
->streams
);
648 ff_format_io_close(s
, &c
->mpd_out
);
649 ff_format_io_close(s
, &c
->m3u8_out
);
652 static void output_segment_list(OutputStream
*os
, AVIOContext
*out
, AVFormatContext
*s
,
653 int representation_id
, int final
)
655 DASHContext
*c
= s
->priv_data
;
656 int i
, start_index
, start_number
;
657 get_start_index_number(os
, c
, &start_index
, &start_number
);
659 if (c
->use_template
) {
660 int timescale
= c
->use_timeline
? os
->ctx
->streams
[0]->time_base
.den
: AV_TIME_BASE
;
661 avio_printf(out
, "\t\t\t\t<SegmentTemplate timescale=\"%d\" ", timescale
);
662 if (!c
->use_timeline
) {
663 avio_printf(out
, "duration=\"%"PRId64
"\" ", os
->seg_duration
);
664 if (c
->streaming
&& os
->availability_time_offset
)
665 avio_printf(out
, "availabilityTimeOffset=\"%.3f\" ",
666 os
->availability_time_offset
);
668 if (c
->streaming
&& os
->availability_time_offset
&& !final
)
669 avio_printf(out
, "availabilityTimeComplete=\"false\" ");
671 avio_printf(out
, "initialization=\"%s\" media=\"%s\" startNumber=\"%d\"", os
->init_seg_name
, os
->media_seg_name
, c
->use_timeline
? start_number
: 1);
672 if (c
->presentation_time_offset
)
673 avio_printf(out
, " presentationTimeOffset=\"%"PRId64
"\"", c
->presentation_time_offset
);
674 avio_printf(out
, ">\n");
675 if (c
->use_timeline
) {
676 int64_t cur_time
= 0;
677 avio_printf(out
, "\t\t\t\t\t<SegmentTimeline>\n");
678 for (i
= start_index
; i
< os
->nb_segments
; ) {
679 Segment
*seg
= os
->segments
[i
];
681 avio_printf(out
, "\t\t\t\t\t\t<S ");
682 if (i
== start_index
|| seg
->time
!= cur_time
) {
683 cur_time
= seg
->time
;
684 avio_printf(out
, "t=\"%"PRId64
"\" ", seg
->time
);
686 avio_printf(out
, "d=\"%"PRId64
"\" ", seg
->duration
);
687 while (i
+ repeat
+ 1 < os
->nb_segments
&&
688 os
->segments
[i
+ repeat
+ 1]->duration
== seg
->duration
&&
689 os
->segments
[i
+ repeat
+ 1]->time
== os
->segments
[i
+ repeat
]->time
+ os
->segments
[i
+ repeat
]->duration
)
692 avio_printf(out
, "r=\"%d\" ", repeat
);
693 avio_printf(out
, "/>\n");
695 cur_time
+= (1 + repeat
) * seg
->duration
;
697 avio_printf(out
, "\t\t\t\t\t</SegmentTimeline>\n");
699 avio_printf(out
, "\t\t\t\t</SegmentTemplate>\n");
700 } else if (c
->single_file
) {
701 avio_printf(out
, "\t\t\t\t<BaseURL>%s</BaseURL>\n", os
->initfile
);
702 avio_printf(out
, "\t\t\t\t<SegmentList timescale=\"%d\" duration=\"%"PRId64
"\" startNumber=\"%d\">\n", AV_TIME_BASE
, FFMIN(os
->seg_duration
, os
->last_duration
), start_number
);
703 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);
704 for (i
= start_index
; i
< os
->nb_segments
; i
++) {
705 Segment
*seg
= os
->segments
[i
];
706 avio_printf(out
, "\t\t\t\t\t<SegmentURL mediaRange=\"%"PRId64
"-%"PRId64
"\" ", seg
->start_pos
, seg
->start_pos
+ seg
->range_length
- 1);
707 if (seg
->index_length
)
708 avio_printf(out
, "indexRange=\"%"PRId64
"-%"PRId64
"\" ", seg
->start_pos
, seg
->start_pos
+ seg
->index_length
- 1);
709 avio_printf(out
, "/>\n");
711 avio_printf(out
, "\t\t\t\t</SegmentList>\n");
713 avio_printf(out
, "\t\t\t\t<SegmentList timescale=\"%d\" duration=\"%"PRId64
"\" startNumber=\"%d\">\n", AV_TIME_BASE
, FFMIN(os
->seg_duration
, os
->last_duration
), start_number
);
714 avio_printf(out
, "\t\t\t\t\t<Initialization sourceURL=\"%s\" />\n", os
->initfile
);
715 for (i
= start_index
; i
< os
->nb_segments
; i
++) {
716 Segment
*seg
= os
->segments
[i
];
717 avio_printf(out
, "\t\t\t\t\t<SegmentURL media=\"%s\" />\n", seg
->file
);
719 avio_printf(out
, "\t\t\t\t</SegmentList>\n");
721 if (!c
->lhls
|| final
) {
722 write_hls_media_playlist(os
, s
, representation_id
, final
, NULL
);
727 static char *xmlescape(const char *str
) {
728 int outlen
= strlen(str
)*3/2 + 6;
729 char *out
= av_realloc(NULL
, outlen
+ 1);
733 for (; *str
; str
++) {
734 if (pos
+ 6 > outlen
) {
736 outlen
= 2 * outlen
+ 6;
737 tmp
= av_realloc(out
, outlen
+ 1);
745 memcpy(&out
[pos
], "&", 5);
747 } else if (*str
== '<') {
748 memcpy(&out
[pos
], "<", 4);
750 } else if (*str
== '>') {
751 memcpy(&out
[pos
], ">", 4);
753 } else if (*str
== '\'') {
754 memcpy(&out
[pos
], "'", 6);
756 } else if (*str
== '\"') {
757 memcpy(&out
[pos
], """, 6);
767 static void write_time(AVIOContext
*out
, int64_t time
)
769 int seconds
= time
/ AV_TIME_BASE
;
770 int fractions
= time
% AV_TIME_BASE
;
771 int minutes
= seconds
/ 60;
772 int hours
= minutes
/ 60;
775 avio_printf(out
, "PT");
777 avio_printf(out
, "%dH", hours
);
778 if (hours
|| minutes
)
779 avio_printf(out
, "%dM", minutes
);
780 avio_printf(out
, "%d.%dS", seconds
, fractions
/ (AV_TIME_BASE
/ 10));
783 static void format_date(char *buf
, int size
, int64_t time_us
)
785 struct tm
*ptm
, tmbuf
;
786 int64_t time_ms
= time_us
/ 1000;
787 const time_t time_s
= time_ms
/ 1000;
788 int millisec
= time_ms
- (time_s
* 1000);
789 ptm
= gmtime_r(&time_s
, &tmbuf
);
792 if (!strftime(buf
, size
, "%Y-%m-%dT%H:%M:%S", ptm
)) {
797 snprintf(buf
+ len
, size
- len
, ".%03dZ", millisec
);
801 static int write_adaptation_set(AVFormatContext
*s
, AVIOContext
*out
, int as_index
,
804 DASHContext
*c
= s
->priv_data
;
805 AdaptationSet
*as
= &c
->as
[as_index
];
806 AVDictionaryEntry
*lang
, *role
;
809 avio_printf(out
, "\t\t<AdaptationSet id=\"%d\" contentType=\"%s\" startWithSAP=\"1\" segmentAlignment=\"true\" bitstreamSwitching=\"true\"",
810 as
->id
, as
->media_type
== AVMEDIA_TYPE_VIDEO
? "video" : "audio");
811 if (as
->media_type
== AVMEDIA_TYPE_VIDEO
&& as
->max_frame_rate
.num
&& !as
->ambiguous_frame_rate
&& av_cmp_q(as
->min_frame_rate
, as
->max_frame_rate
) < 0)
812 avio_printf(out
, " maxFrameRate=\"%d/%d\"", as
->max_frame_rate
.num
, as
->max_frame_rate
.den
);
813 else if (as
->media_type
== AVMEDIA_TYPE_VIDEO
&& as
->max_frame_rate
.num
&& !as
->ambiguous_frame_rate
&& !av_cmp_q(as
->min_frame_rate
, as
->max_frame_rate
))
814 avio_printf(out
, " frameRate=\"%d/%d\"", as
->max_frame_rate
.num
, as
->max_frame_rate
.den
);
815 if (as
->media_type
== AVMEDIA_TYPE_VIDEO
) {
816 avio_printf(out
, " maxWidth=\"%d\" maxHeight=\"%d\"", as
->max_width
, as
->max_height
);
817 avio_printf(out
, " par=\"%d:%d\"", as
->par
.num
, as
->par
.den
);
819 lang
= av_dict_get(as
->metadata
, "language", NULL
, 0);
821 avio_printf(out
, " lang=\"%s\"", lang
->value
);
822 avio_printf(out
, ">\n");
824 if (!final
&& c
->ldash
&& as
->max_frag_duration
&& !(c
->profile
& MPD_PROFILE_DVB
))
825 avio_printf(out
, "\t\t\t<Resync dT=\"%"PRId64
"\" type=\"0\"/>\n", as
->max_frag_duration
);
826 if (as
->trick_idx
>= 0)
827 avio_printf(out
, "\t\t\t<EssentialProperty id=\"%d\" schemeIdUri=\"http://dashif.org/guidelines/trickmode\" value=\"%d\"/>\n", as
->id
, as
->trick_idx
);
828 role
= av_dict_get(as
->metadata
, "role", NULL
, 0);
830 avio_printf(out
, "\t\t\t<Role schemeIdUri=\"urn:mpeg:dash:role:2011\" value=\"%s\"/>\n", role
->value
);
832 avio_printf(out
, "\t\t\t%s\n", as
->descriptor
);
833 for (i
= 0; i
< s
->nb_streams
; i
++) {
834 AVStream
*st
= s
->streams
[i
];
835 OutputStream
*os
= &c
->streams
[i
];
836 char bandwidth_str
[64] = {'\0'};
838 if (os
->as_idx
- 1 != as_index
)
841 if (os
->bit_rate
> 0)
842 snprintf(bandwidth_str
, sizeof(bandwidth_str
), " bandwidth=\"%d\"",
845 if (as
->media_type
== AVMEDIA_TYPE_VIDEO
) {
846 avio_printf(out
, "\t\t\t<Representation id=\"%d\" mimeType=\"video/%s\" codecs=\"%s\"%s width=\"%d\" height=\"%d\"",
847 i
, os
->format_name
, os
->codec_str
, bandwidth_str
, s
->streams
[i
]->codecpar
->width
, s
->streams
[i
]->codecpar
->height
);
848 if (st
->codecpar
->field_order
== AV_FIELD_UNKNOWN
)
849 avio_printf(out
, " scanType=\"unknown\"");
850 else if (st
->codecpar
->field_order
!= AV_FIELD_PROGRESSIVE
)
851 avio_printf(out
, " scanType=\"interlaced\"");
852 avio_printf(out
, " sar=\"%d:%d\"", os
->sar
.num
, os
->sar
.den
);
853 if (st
->avg_frame_rate
.num
&& av_cmp_q(as
->min_frame_rate
, as
->max_frame_rate
) < 0)
854 avio_printf(out
, " frameRate=\"%d/%d\"", st
->avg_frame_rate
.num
, st
->avg_frame_rate
.den
);
855 if (as
->trick_idx
>= 0) {
856 AdaptationSet
*tas
= &c
->as
[as
->trick_idx
];
857 if (!as
->ambiguous_frame_rate
&& !tas
->ambiguous_frame_rate
)
858 avio_printf(out
, " maxPlayoutRate=\"%d\"", FFMAX((int)av_q2d(av_div_q(tas
->min_frame_rate
, as
->min_frame_rate
)), 1));
860 if (!os
->coding_dependency
)
861 avio_printf(out
, " codingDependency=\"false\"");
862 avio_printf(out
, ">\n");
864 avio_printf(out
, "\t\t\t<Representation id=\"%d\" mimeType=\"audio/%s\" codecs=\"%s\"%s audioSamplingRate=\"%d\">\n",
865 i
, os
->format_name
, os
->codec_str
, bandwidth_str
, s
->streams
[i
]->codecpar
->sample_rate
);
866 avio_printf(out
, "\t\t\t\t<AudioChannelConfiguration schemeIdUri=\"urn:mpeg:dash:23003:3:audio_channel_configuration:2011\" value=\"%d\" />\n",
867 s
->streams
[i
]->codecpar
->channels
);
869 if (!final
&& c
->write_prft
&& os
->producer_reference_time_str
[0]) {
870 avio_printf(out
, "\t\t\t\t<ProducerReferenceTime id=\"%d\" inband=\"true\" type=\"%s\" wallClockTime=\"%s\" presentationTime=\"%"PRId64
"\">\n",
871 i
, os
->producer_reference_time
.flags
? "captured" : "encoder", os
->producer_reference_time_str
, c
->presentation_time_offset
);
872 avio_printf(out
, "\t\t\t\t\t<UTCTiming schemeIdUri=\"urn:mpeg:dash:utc:http-xsdate:2014\" value=\"%s\"/>\n", c
->utc_timing_url
);
873 avio_printf(out
, "\t\t\t\t</ProducerReferenceTime>\n");
875 if (!final
&& c
->ldash
&& os
->gop_size
&& os
->frag_type
!= FRAG_TYPE_NONE
&& !(c
->profile
& MPD_PROFILE_DVB
) &&
876 (os
->frag_type
!= FRAG_TYPE_DURATION
|| os
->frag_duration
!= os
->seg_duration
))
877 avio_printf(out
, "\t\t\t\t<Resync dT=\"%"PRId64
"\" type=\"1\"/>\n", os
->gop_size
);
878 output_segment_list(os
, out
, s
, i
, final
);
879 avio_printf(out
, "\t\t\t</Representation>\n");
881 avio_printf(out
, "\t\t</AdaptationSet>\n");
886 static int add_adaptation_set(AVFormatContext
*s
, AdaptationSet
**as
, enum AVMediaType type
)
888 DASHContext
*c
= s
->priv_data
;
891 if (c
->profile
& MPD_PROFILE_DVB
&& (c
->nb_as
+ 1) > 16) {
892 av_log(s
, AV_LOG_ERROR
, "DVB-DASH profile allows a max of 16 Adaptation Sets\n");
893 return AVERROR(EINVAL
);
895 mem
= av_realloc(c
->as
, sizeof(*c
->as
) * (c
->nb_as
+ 1));
897 return AVERROR(ENOMEM
);
901 *as
= &c
->as
[c
->nb_as
- 1];
902 memset(*as
, 0, sizeof(**as
));
903 (*as
)->media_type
= type
;
904 (*as
)->frag_type
= -1;
905 (*as
)->trick_idx
= -1;
910 static int adaptation_set_add_stream(AVFormatContext
*s
, int as_idx
, int i
)
912 DASHContext
*c
= s
->priv_data
;
913 AdaptationSet
*as
= &c
->as
[as_idx
- 1];
914 OutputStream
*os
= &c
->streams
[i
];
916 if (as
->media_type
!= s
->streams
[i
]->codecpar
->codec_type
) {
917 av_log(s
, AV_LOG_ERROR
, "Codec type of stream %d doesn't match AdaptationSet's media type\n", i
);
918 return AVERROR(EINVAL
);
919 } else if (os
->as_idx
) {
920 av_log(s
, AV_LOG_ERROR
, "Stream %d is already assigned to an AdaptationSet\n", i
);
921 return AVERROR(EINVAL
);
923 if (c
->profile
& MPD_PROFILE_DVB
&& (as
->nb_streams
+ 1) > 16) {
924 av_log(s
, AV_LOG_ERROR
, "DVB-DASH profile allows a max of 16 Representations per Adaptation Set\n");
925 return AVERROR(EINVAL
);
933 static int parse_adaptation_sets(AVFormatContext
*s
)
935 DASHContext
*c
= s
->priv_data
;
936 const char *p
= c
->adaptation_sets
;
937 enum { new_set
, parse_default
, parsing_streams
, parse_seg_duration
, parse_frag_duration
} state
;
941 // default: one AdaptationSet for each stream
943 for (i
= 0; i
< s
->nb_streams
; i
++) {
944 if ((ret
= add_adaptation_set(s
, &as
, s
->streams
[i
]->codecpar
->codec_type
)) < 0)
948 c
->streams
[i
].as_idx
= c
->nb_as
;
954 // syntax id=0,streams=0,1,2 id=1,streams=3,4 and so on
955 // option id=0,descriptor=descriptor_str,streams=0,1,2 and so on
956 // option id=0,seg_duration=2.5,frag_duration=0.5,streams=0,1,2
957 // id=1,trick_id=0,seg_duration=10,frag_type=none,streams=3 and so on
958 // descriptor is useful to the scheme defined by ISO/IEC 23009-1:2014/Amd.2:2015
959 // descriptor_str should be a self-closing xml tag.
960 // seg_duration and frag_duration have the same syntax as the global options of
961 // the same name, and the former have precedence over them if set.
967 } else if (state
== new_set
&& av_strstart(p
, "id=", &p
)) {
968 char id_str
[10], *end_str
;
971 snprintf(id_str
, sizeof(id_str
), "%.*s", n
, p
);
973 i
= strtol(id_str
, &end_str
, 10);
974 if (id_str
== end_str
|| i
< 0 || i
> c
->nb_as
) {
975 av_log(s
, AV_LOG_ERROR
, "\"%s\" is not a valid value for an AdaptationSet id\n", id_str
);
976 return AVERROR(EINVAL
);
979 if ((ret
= add_adaptation_set(s
, &as
, AVMEDIA_TYPE_UNKNOWN
)) < 0)
986 state
= parse_default
;
987 } else if (state
!= new_set
&& av_strstart(p
, "seg_duration=", &p
)) {
988 state
= parse_seg_duration
;
989 } else if (state
!= new_set
&& av_strstart(p
, "frag_duration=", &p
)) {
990 state
= parse_frag_duration
;
991 } else if (state
== parse_seg_duration
|| state
== parse_frag_duration
) {
996 snprintf(str
, sizeof(str
), "%.*s", n
, p
);
1001 ret
= av_parse_time(&usecs
, str
, 1);
1003 av_log(s
, AV_LOG_ERROR
, "Unable to parse option value \"%s\" as duration\n", str
);
1007 if (state
== parse_seg_duration
)
1008 as
->seg_duration
= usecs
;
1010 as
->frag_duration
= usecs
;
1011 state
= parse_default
;
1012 } else if (state
!= new_set
&& av_strstart(p
, "frag_type=", &p
)) {
1015 n
= strcspn(p
, ",");
1016 snprintf(type_str
, sizeof(type_str
), "%.*s", n
, p
);
1021 if (!strcmp(type_str
, "duration"))
1022 as
->frag_type
= FRAG_TYPE_DURATION
;
1023 else if (!strcmp(type_str
, "pframes"))
1024 as
->frag_type
= FRAG_TYPE_PFRAMES
;
1025 else if (!strcmp(type_str
, "every_frame"))
1026 as
->frag_type
= FRAG_TYPE_EVERY_FRAME
;
1027 else if (!strcmp(type_str
, "none"))
1028 as
->frag_type
= FRAG_TYPE_NONE
;
1030 av_log(s
, AV_LOG_ERROR
, "Unable to parse option value \"%s\" as fragment type\n", type_str
);
1033 state
= parse_default
;
1034 } else if (state
!= new_set
&& av_strstart(p
, "descriptor=", &p
)) {
1035 n
= strcspn(p
, ">") + 1; //followed by one comma, so plus 1
1036 if (n
< strlen(p
)) {
1037 as
->descriptor
= av_strndup(p
, n
);
1039 av_log(s
, AV_LOG_ERROR
, "Parse error, descriptor string should be a self-closing xml tag\n");
1040 return AVERROR(EINVAL
);
1045 state
= parse_default
;
1046 } else if ((state
!= new_set
) && av_strstart(p
, "trick_id=", &p
)) {
1047 char trick_id_str
[10], *end_str
;
1049 n
= strcspn(p
, ",");
1050 snprintf(trick_id_str
, sizeof(trick_id_str
), "%.*s", n
, p
);
1053 as
->trick_idx
= strtol(trick_id_str
, &end_str
, 10);
1054 if (trick_id_str
== end_str
|| as
->trick_idx
< 0)
1055 return AVERROR(EINVAL
);
1059 state
= parse_default
;
1060 } else if ((state
!= new_set
) && av_strstart(p
, "streams=", &p
)) { //descriptor and durations are optional
1061 state
= parsing_streams
;
1062 } else if (state
== parsing_streams
) {
1063 AdaptationSet
*as
= &c
->as
[c
->nb_as
- 1];
1064 char idx_str
[8], *end_str
;
1066 n
= strcspn(p
, " ,");
1067 snprintf(idx_str
, sizeof(idx_str
), "%.*s", n
, p
);
1070 // if value is "a" or "v", map all streams of that type
1071 if (as
->media_type
== AVMEDIA_TYPE_UNKNOWN
&& (idx_str
[0] == 'v' || idx_str
[0] == 'a')) {
1072 enum AVMediaType type
= (idx_str
[0] == 'v') ? AVMEDIA_TYPE_VIDEO
: AVMEDIA_TYPE_AUDIO
;
1073 av_log(s
, AV_LOG_DEBUG
, "Map all streams of type %s\n", idx_str
);
1075 for (i
= 0; i
< s
->nb_streams
; i
++) {
1076 if (s
->streams
[i
]->codecpar
->codec_type
!= type
)
1079 as
->media_type
= s
->streams
[i
]->codecpar
->codec_type
;
1081 if ((ret
= adaptation_set_add_stream(s
, c
->nb_as
, i
)) < 0)
1084 } else { // select single stream
1085 i
= strtol(idx_str
, &end_str
, 10);
1086 if (idx_str
== end_str
|| i
< 0 || i
>= s
->nb_streams
) {
1087 av_log(s
, AV_LOG_ERROR
, "Selected stream \"%s\" not found!\n", idx_str
);
1088 return AVERROR(EINVAL
);
1090 av_log(s
, AV_LOG_DEBUG
, "Map stream %d\n", i
);
1092 if (as
->media_type
== AVMEDIA_TYPE_UNKNOWN
) {
1093 as
->media_type
= s
->streams
[i
]->codecpar
->codec_type
;
1096 if ((ret
= adaptation_set_add_stream(s
, c
->nb_as
, i
)) < 0)
1105 return AVERROR(EINVAL
);
1110 // check for unassigned streams
1111 for (i
= 0; i
< s
->nb_streams
; i
++) {
1112 OutputStream
*os
= &c
->streams
[i
];
1114 av_log(s
, AV_LOG_ERROR
, "Stream %d is not mapped to an AdaptationSet\n", i
);
1115 return AVERROR(EINVAL
);
1119 // check references for trick mode AdaptationSet
1120 for (i
= 0; i
< c
->nb_as
; i
++) {
1122 if (as
->trick_idx
< 0)
1124 for (n
= 0; n
< c
->nb_as
; n
++) {
1125 if (c
->as
[n
].id
== as
->trick_idx
)
1128 if (n
>= c
->nb_as
) {
1129 av_log(s
, AV_LOG_ERROR
, "reference AdaptationSet id \"%d\" not found for trick mode AdaptationSet id \"%d\"\n", as
->trick_idx
, as
->id
);
1130 return AVERROR(EINVAL
);
1137 static int write_manifest(AVFormatContext
*s
, int final
)
1139 DASHContext
*c
= s
->priv_data
;
1141 char temp_filename
[1024];
1143 const char *proto
= avio_find_protocol_name(s
->url
);
1144 int use_rename
= proto
&& !strcmp(proto
, "file");
1145 static unsigned int warned_non_file
= 0;
1146 AVDictionaryEntry
*title
= av_dict_get(s
->metadata
, "title", NULL
, 0);
1147 AVDictionary
*opts
= NULL
;
1149 if (!use_rename
&& !warned_non_file
++)
1150 av_log(s
, AV_LOG_ERROR
, "Cannot use rename on non file protocol, this may lead to races and temporary partial files\n");
1152 snprintf(temp_filename
, sizeof(temp_filename
), use_rename
? "%s.tmp" : "%s", s
->url
);
1153 set_http_options(&opts
, c
);
1154 ret
= dashenc_io_open(s
, &c
->mpd_out
, temp_filename
, &opts
);
1155 av_dict_free(&opts
);
1157 return handle_io_open_error(s
, ret
, temp_filename
);
1160 avio_printf(out
, "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
1161 avio_printf(out
, "<MPD xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n"
1162 "\txmlns=\"urn:mpeg:dash:schema:mpd:2011\"\n"
1163 "\txmlns:xlink=\"http://www.w3.org/1999/xlink\"\n"
1164 "\txsi:schemaLocation=\"urn:mpeg:DASH:schema:MPD:2011 http://standards.iso.org/ittf/PubliclyAvailableStandards/MPEG-DASH_schema_files/DASH-MPD.xsd\"\n"
1166 if (c
->profile
& MPD_PROFILE_DASH
)
1167 avio_printf(out
, "%s%s", "urn:mpeg:dash:profile:isoff-live:2011", c
->profile
& MPD_PROFILE_DVB
? "," : "\"\n");
1168 if (c
->profile
& MPD_PROFILE_DVB
)
1169 avio_printf(out
, "%s", "urn:dvb:dash:profile:dvb-dash:2014\"\n");
1170 avio_printf(out
, "\ttype=\"%s\"\n",
1171 final
? "static" : "dynamic");
1173 avio_printf(out
, "\tmediaPresentationDuration=\"");
1174 write_time(out
, c
->total_duration
);
1175 avio_printf(out
, "\"\n");
1177 int64_t update_period
= c
->last_duration
/ AV_TIME_BASE
;
1179 if (c
->use_template
&& !c
->use_timeline
)
1180 update_period
= 500;
1181 avio_printf(out
, "\tminimumUpdatePeriod=\"PT%"PRId64
"S\"\n", update_period
);
1183 avio_printf(out
, "\tsuggestedPresentationDelay=\"PT%"PRId64
"S\"\n", c
->last_duration
/ AV_TIME_BASE
);
1184 if (c
->availability_start_time
[0])
1185 avio_printf(out
, "\tavailabilityStartTime=\"%s\"\n", c
->availability_start_time
);
1186 format_date(now_str
, sizeof(now_str
), av_gettime());
1188 avio_printf(out
, "\tpublishTime=\"%s\"\n", now_str
);
1189 if (c
->window_size
&& c
->use_template
) {
1190 avio_printf(out
, "\ttimeShiftBufferDepth=\"");
1191 write_time(out
, c
->last_duration
* c
->window_size
);
1192 avio_printf(out
, "\"\n");
1195 avio_printf(out
, "\tmaxSegmentDuration=\"");
1196 write_time(out
, c
->max_segment_duration
);
1197 avio_printf(out
, "\"\n");
1198 avio_printf(out
, "\tminBufferTime=\"");
1199 write_time(out
, c
->ldash
&& c
->max_gop_size
? c
->max_gop_size
: c
->last_duration
* 2);
1200 avio_printf(out
, "\">\n");
1201 avio_printf(out
, "\t<ProgramInformation>\n");
1203 char *escaped
= xmlescape(title
->value
);
1204 avio_printf(out
, "\t\t<Title>%s</Title>\n", escaped
);
1207 avio_printf(out
, "\t</ProgramInformation>\n");
1209 avio_printf(out
, "\t<ServiceDescription id=\"0\">\n");
1210 if (!final
&& c
->target_latency
&& c
->target_latency_refid
>= 0) {
1211 avio_printf(out
, "\t\t<Latency target=\"%"PRId64
"\"", c
->target_latency
/ 1000);
1212 if (s
->nb_streams
> 1)
1213 avio_printf(out
, " referenceId=\"%d\"", c
->target_latency_refid
);
1214 avio_printf(out
, "/>\n");
1216 if (av_cmp_q(c
->min_playback_rate
, (AVRational
) {1, 1}) ||
1217 av_cmp_q(c
->max_playback_rate
, (AVRational
) {1, 1}))
1218 avio_printf(out
, "\t\t<PlaybackRate min=\"%.2f\" max=\"%.2f\"/>\n",
1219 av_q2d(c
->min_playback_rate
), av_q2d(c
->max_playback_rate
));
1220 avio_printf(out
, "\t</ServiceDescription>\n");
1222 if (c
->window_size
&& s
->nb_streams
> 0 && c
->streams
[0].nb_segments
> 0 && !c
->use_template
) {
1223 OutputStream
*os
= &c
->streams
[0];
1224 int start_index
= FFMAX(os
->nb_segments
- c
->window_size
, 0);
1225 int64_t start_time
= av_rescale_q(os
->segments
[start_index
]->time
, s
->streams
[0]->time_base
, AV_TIME_BASE_Q
);
1226 avio_printf(out
, "\t<Period id=\"0\" start=\"");
1227 write_time(out
, start_time
);
1228 avio_printf(out
, "\">\n");
1230 avio_printf(out
, "\t<Period id=\"0\" start=\"PT0.0S\">\n");
1233 for (i
= 0; i
< c
->nb_as
; i
++) {
1234 if ((ret
= write_adaptation_set(s
, out
, i
, final
)) < 0)
1237 avio_printf(out
, "\t</Period>\n");
1239 if (c
->utc_timing_url
)
1240 avio_printf(out
, "\t<UTCTiming schemeIdUri=\"urn:mpeg:dash:utc:http-xsdate:2014\" value=\"%s\"/>\n", c
->utc_timing_url
);
1242 avio_printf(out
, "</MPD>\n");
1244 dashenc_io_close(s
, &c
->mpd_out
, temp_filename
);
1247 if ((ret
= ff_rename(temp_filename
, s
->url
, s
)) < 0)
1251 if (c
->hls_playlist
) {
1252 char filename_hls
[1024];
1253 const char *audio_group
= "A1";
1254 char audio_codec_str
[128] = "\0";
1256 int max_audio_bitrate
= 0;
1258 // Publish master playlist only the configured rate
1259 if (c
->master_playlist_created
&& (!c
->master_publish_rate
||
1260 c
->streams
[0].segment_index
% c
->master_publish_rate
))
1264 snprintf(filename_hls
, sizeof(filename_hls
), "%smaster.m3u8", c
->dirname
);
1266 snprintf(filename_hls
, sizeof(filename_hls
), "master.m3u8");
1268 snprintf(temp_filename
, sizeof(temp_filename
), use_rename
? "%s.tmp" : "%s", filename_hls
);
1270 set_http_options(&opts
, c
);
1271 ret
= dashenc_io_open(s
, &c
->m3u8_out
, temp_filename
, &opts
);
1272 av_dict_free(&opts
);
1274 return handle_io_open_error(s
, ret
, temp_filename
);
1277 ff_hls_write_playlist_version(c
->m3u8_out
, 7);
1279 for (i
= 0; i
< s
->nb_streams
; i
++) {
1280 char playlist_file
[64];
1281 AVStream
*st
= s
->streams
[i
];
1282 OutputStream
*os
= &c
->streams
[i
];
1283 if (st
->codecpar
->codec_type
!= AVMEDIA_TYPE_AUDIO
)
1285 if (os
->segment_type
!= SEGMENT_TYPE_MP4
)
1287 get_hls_playlist_name(playlist_file
, sizeof(playlist_file
), NULL
, i
);
1288 ff_hls_write_audio_rendition(c
->m3u8_out
, (char *)audio_group
,
1289 playlist_file
, NULL
, i
, is_default
);
1290 max_audio_bitrate
= FFMAX(st
->codecpar
->bit_rate
+
1291 os
->muxer_overhead
, max_audio_bitrate
);
1292 if (!av_strnstr(audio_codec_str
, os
->codec_str
, sizeof(audio_codec_str
))) {
1293 if (strlen(audio_codec_str
))
1294 av_strlcat(audio_codec_str
, ",", sizeof(audio_codec_str
));
1295 av_strlcat(audio_codec_str
, os
->codec_str
, sizeof(audio_codec_str
));
1300 for (i
= 0; i
< s
->nb_streams
; i
++) {
1301 char playlist_file
[64];
1302 char codec_str
[128];
1303 AVStream
*st
= s
->streams
[i
];
1304 OutputStream
*os
= &c
->streams
[i
];
1305 char *agroup
= NULL
;
1306 char *codec_str_ptr
= NULL
;
1307 int stream_bitrate
= st
->codecpar
->bit_rate
+ os
->muxer_overhead
;
1308 if (st
->codecpar
->codec_type
!= AVMEDIA_TYPE_VIDEO
)
1310 if (os
->segment_type
!= SEGMENT_TYPE_MP4
)
1312 av_strlcpy(codec_str
, os
->codec_str
, sizeof(codec_str
));
1313 if (max_audio_bitrate
) {
1314 agroup
= (char *)audio_group
;
1315 stream_bitrate
+= max_audio_bitrate
;
1316 av_strlcat(codec_str
, ",", sizeof(codec_str
));
1317 av_strlcat(codec_str
, audio_codec_str
, sizeof(codec_str
));
1319 if (st
->codecpar
->codec_id
!= AV_CODEC_ID_HEVC
) {
1320 codec_str_ptr
= codec_str
;
1322 get_hls_playlist_name(playlist_file
, sizeof(playlist_file
), NULL
, i
);
1323 ff_hls_write_stream_info(st
, c
->m3u8_out
, stream_bitrate
,
1324 playlist_file
, agroup
,
1325 codec_str_ptr
, NULL
, NULL
);
1327 dashenc_io_close(s
, &c
->m3u8_out
, temp_filename
);
1329 if ((ret
= ff_rename(temp_filename
, filename_hls
, s
)) < 0)
1331 c
->master_playlist_created
= 1;
1337 static int dict_copy_entry(AVDictionary
**dst
, const AVDictionary
*src
, const char *key
)
1339 AVDictionaryEntry
*entry
= av_dict_get(src
, key
, NULL
, 0);
1341 av_dict_set(dst
, key
, entry
->value
, AV_DICT_DONT_OVERWRITE
);
1345 static int dash_init(AVFormatContext
*s
)
1347 DASHContext
*c
= s
->priv_data
;
1350 char basename
[1024];
1352 c
->nr_of_streams_to_flush
= 0;
1353 if (c
->single_file_name
)
1356 c
->use_template
= 0;
1359 av_log(s
, AV_LOG_ERROR
, "At least one profile must be enabled.\n");
1360 return AVERROR(EINVAL
);
1362 #if FF_API_DASH_MIN_SEG_DURATION
1363 if (c
->min_seg_duration
!= 5000000) {
1364 av_log(s
, AV_LOG_WARNING
, "The min_seg_duration option is deprecated and will be removed. Please use the -seg_duration\n");
1365 c
->seg_duration
= c
->min_seg_duration
;
1368 if (c
->lhls
&& s
->strict_std_compliance
> FF_COMPLIANCE_EXPERIMENTAL
) {
1369 av_log(s
, AV_LOG_ERROR
,
1370 "LHLS is experimental, Please set -strict experimental in order to enable it.\n");
1371 return AVERROR_EXPERIMENTAL
;
1374 if (c
->lhls
&& !c
->streaming
) {
1375 av_log(s
, AV_LOG_WARNING
, "LHLS option will be ignored as streaming is not enabled\n");
1379 if (c
->lhls
&& !c
->hls_playlist
) {
1380 av_log(s
, AV_LOG_WARNING
, "LHLS option will be ignored as hls_playlist is not enabled\n");
1384 if (c
->ldash
&& !c
->streaming
) {
1385 av_log(s
, AV_LOG_WARNING
, "LDash option will be ignored as streaming is not enabled\n");
1389 if (c
->target_latency
&& !c
->streaming
) {
1390 av_log(s
, AV_LOG_WARNING
, "Target latency option will be ignored as streaming is not enabled\n");
1391 c
->target_latency
= 0;
1394 if (c
->global_sidx
&& !c
->single_file
) {
1395 av_log(s
, AV_LOG_WARNING
, "Global SIDX option will be ignored as single_file is not enabled\n");
1399 if (c
->global_sidx
&& c
->streaming
) {
1400 av_log(s
, AV_LOG_WARNING
, "Global SIDX option will be ignored as streaming is enabled\n");
1403 if (c
->frag_type
== FRAG_TYPE_NONE
&& c
->streaming
) {
1404 av_log(s
, AV_LOG_VERBOSE
, "Changing frag_type from none to every_frame as streaming is enabled\n");
1405 c
->frag_type
= FRAG_TYPE_EVERY_FRAME
;
1408 if (c
->write_prft
< 0) {
1409 c
->write_prft
= c
->ldash
;
1411 av_log(s
, AV_LOG_VERBOSE
, "Enabling Producer Reference Time element for Low Latency mode\n");
1414 if (c
->write_prft
&& !c
->utc_timing_url
) {
1415 av_log(s
, AV_LOG_WARNING
, "Producer Reference Time element option will be ignored as utc_timing_url is not set\n");
1419 if (c
->write_prft
&& !c
->streaming
) {
1420 av_log(s
, AV_LOG_WARNING
, "Producer Reference Time element option will be ignored as streaming is not enabled\n");
1424 if (c
->ldash
&& !c
->write_prft
) {
1425 av_log(s
, AV_LOG_WARNING
, "Low Latency mode enabled without Producer Reference Time element option! Resulting manifest may not be complaint\n");
1428 if (c
->target_latency
&& !c
->write_prft
) {
1429 av_log(s
, AV_LOG_WARNING
, "Target latency option will be ignored as Producer Reference Time element will not be written\n");
1430 c
->target_latency
= 0;
1433 if (av_cmp_q(c
->max_playback_rate
, c
->min_playback_rate
) < 0) {
1434 av_log(s
, AV_LOG_WARNING
, "Minimum playback rate value is higer than the Maximum. Both will be ignored\n");
1435 c
->min_playback_rate
= c
->max_playback_rate
= (AVRational
) {1, 1};
1438 av_strlcpy(c
->dirname
, s
->url
, sizeof(c
->dirname
));
1439 ptr
= strrchr(c
->dirname
, '/');
1441 av_strlcpy(basename
, &ptr
[1], sizeof(basename
));
1444 c
->dirname
[0] = '\0';
1445 av_strlcpy(basename
, s
->url
, sizeof(basename
));
1448 ptr
= strrchr(basename
, '.');
1452 c
->streams
= av_mallocz(sizeof(*c
->streams
) * s
->nb_streams
);
1454 return AVERROR(ENOMEM
);
1456 if ((ret
= parse_adaptation_sets(s
)) < 0)
1459 if ((ret
= init_segment_types(s
)) < 0)
1462 for (i
= 0; i
< s
->nb_streams
; i
++) {
1463 OutputStream
*os
= &c
->streams
[i
];
1464 AdaptationSet
*as
= &c
->as
[os
->as_idx
- 1];
1465 AVFormatContext
*ctx
;
1467 AVDictionary
*opts
= NULL
;
1468 char filename
[1024];
1470 os
->bit_rate
= s
->streams
[i
]->codecpar
->bit_rate
;
1471 if (!os
->bit_rate
) {
1472 int level
= s
->strict_std_compliance
>= FF_COMPLIANCE_STRICT
?
1473 AV_LOG_ERROR
: AV_LOG_WARNING
;
1474 av_log(s
, level
, "No bit rate set for stream %d\n", i
);
1475 if (s
->strict_std_compliance
>= FF_COMPLIANCE_STRICT
)
1476 return AVERROR(EINVAL
);
1479 // copy AdaptationSet language and role from stream metadata
1480 dict_copy_entry(&as
->metadata
, s
->streams
[i
]->metadata
, "language");
1481 dict_copy_entry(&as
->metadata
, s
->streams
[i
]->metadata
, "role");
1483 if (c
->init_seg_name
) {
1484 os
->init_seg_name
= av_strireplace(c
->init_seg_name
, "$ext$", os
->extension_name
);
1485 if (!os
->init_seg_name
)
1486 return AVERROR(ENOMEM
);
1488 if (c
->media_seg_name
) {
1489 os
->media_seg_name
= av_strireplace(c
->media_seg_name
, "$ext$", os
->extension_name
);
1490 if (!os
->media_seg_name
)
1491 return AVERROR(ENOMEM
);
1493 if (c
->single_file_name
) {
1494 os
->single_file_name
= av_strireplace(c
->single_file_name
, "$ext$", os
->extension_name
);
1495 if (!os
->single_file_name
)
1496 return AVERROR(ENOMEM
);
1499 if (os
->segment_type
== SEGMENT_TYPE_WEBM
) {
1500 if ((!c
->single_file
&& check_file_extension(os
->init_seg_name
, os
->format_name
) != 0) ||
1501 (!c
->single_file
&& check_file_extension(os
->media_seg_name
, os
->format_name
) != 0) ||
1502 (c
->single_file
&& check_file_extension(os
->single_file_name
, os
->format_name
) != 0)) {
1503 av_log(s
, AV_LOG_WARNING
,
1504 "One or many segment file names doesn't end with .webm. "
1505 "Override -init_seg_name and/or -media_seg_name and/or "
1506 "-single_file_name to end with the extension .webm\n");
1509 // Streaming not supported as matroskaenc buffers internally before writing the output
1510 av_log(s
, AV_LOG_WARNING
, "One or more streams in WebM output format. Streaming option will be ignored\n");
1515 os
->ctx
= ctx
= avformat_alloc_context();
1517 return AVERROR(ENOMEM
);
1519 ctx
->oformat
= av_guess_format(os
->format_name
, NULL
, NULL
);
1521 return AVERROR_MUXER_NOT_FOUND
;
1522 ctx
->interrupt_callback
= s
->interrupt_callback
;
1523 ctx
->opaque
= s
->opaque
;
1524 ctx
->io_close
= s
->io_close
;
1525 ctx
->io_open
= s
->io_open
;
1526 ctx
->strict_std_compliance
= s
->strict_std_compliance
;
1528 if (!(st
= avformat_new_stream(ctx
, NULL
)))
1529 return AVERROR(ENOMEM
);
1530 avcodec_parameters_copy(st
->codecpar
, s
->streams
[i
]->codecpar
);
1531 st
->sample_aspect_ratio
= s
->streams
[i
]->sample_aspect_ratio
;
1532 st
->time_base
= s
->streams
[i
]->time_base
;
1533 st
->avg_frame_rate
= s
->streams
[i
]->avg_frame_rate
;
1534 ctx
->avoid_negative_ts
= s
->avoid_negative_ts
;
1535 ctx
->flags
= s
->flags
;
1537 os
->parser
= av_parser_init(st
->codecpar
->codec_id
);
1539 os
->parser_avctx
= avcodec_alloc_context3(NULL
);
1540 if (!os
->parser_avctx
)
1541 return AVERROR(ENOMEM
);
1542 ret
= avcodec_parameters_to_context(os
->parser_avctx
, st
->codecpar
);
1545 // We only want to parse frame headers
1546 os
->parser
->flags
|= PARSER_FLAG_COMPLETE_FRAMES
;
1549 if (c
->single_file
) {
1550 if (os
->single_file_name
)
1551 ff_dash_fill_tmpl_params(os
->initfile
, sizeof(os
->initfile
), os
->single_file_name
, i
, 0, os
->bit_rate
, 0);
1553 snprintf(os
->initfile
, sizeof(os
->initfile
), "%s-stream%d.%s", basename
, i
, os
->format_name
);
1555 ff_dash_fill_tmpl_params(os
->initfile
, sizeof(os
->initfile
), os
->init_seg_name
, i
, 0, os
->bit_rate
, 0);
1557 snprintf(filename
, sizeof(filename
), "%s%s", c
->dirname
, os
->initfile
);
1558 set_http_options(&opts
, c
);
1559 if (!c
->single_file
) {
1560 if ((ret
= avio_open_dyn_buf(&ctx
->pb
)) < 0)
1562 ret
= s
->io_open(s
, &os
->out
, filename
, AVIO_FLAG_WRITE
, &opts
);
1564 ctx
->url
= av_strdup(filename
);
1565 ret
= avio_open2(&ctx
->pb
, filename
, AVIO_FLAG_WRITE
, NULL
, &opts
);
1567 av_dict_free(&opts
);
1570 os
->init_start_pos
= 0;
1572 av_dict_copy(&opts
, c
->format_options
, 0);
1573 if (!as
->seg_duration
)
1574 as
->seg_duration
= c
->seg_duration
;
1575 if (!as
->frag_duration
)
1576 as
->frag_duration
= c
->frag_duration
;
1577 if (as
->frag_type
< 0)
1578 as
->frag_type
= c
->frag_type
;
1579 os
->seg_duration
= as
->seg_duration
;
1580 os
->frag_duration
= as
->frag_duration
;
1581 os
->frag_type
= as
->frag_type
;
1583 c
->max_segment_duration
= FFMAX(c
->max_segment_duration
, as
->seg_duration
);
1585 if (c
->profile
& MPD_PROFILE_DVB
&& (os
->seg_duration
> 15000000 || os
->seg_duration
< 960000)) {
1586 av_log(s
, AV_LOG_ERROR
, "Segment duration %"PRId64
" is outside the allowed range for DVB-DASH profile\n", os
->seg_duration
);
1587 return AVERROR(EINVAL
);
1590 if (os
->frag_type
== FRAG_TYPE_DURATION
&& !os
->frag_duration
) {
1591 av_log(s
, AV_LOG_WARNING
, "frag_type set to duration for stream %d but no frag_duration set\n", i
);
1592 os
->frag_type
= c
->streaming
? FRAG_TYPE_EVERY_FRAME
: FRAG_TYPE_NONE
;
1594 if (os
->frag_type
== FRAG_TYPE_DURATION
&& os
->frag_duration
> os
->seg_duration
) {
1595 av_log(s
, AV_LOG_ERROR
, "Fragment duration %"PRId64
" is longer than Segment duration %"PRId64
"\n", os
->frag_duration
, os
->seg_duration
);
1596 return AVERROR(EINVAL
);
1598 if (os
->frag_type
== FRAG_TYPE_PFRAMES
&& (st
->codecpar
->codec_type
!= AVMEDIA_TYPE_VIDEO
|| !os
->parser
)) {
1599 if (st
->codecpar
->codec_type
== AVMEDIA_TYPE_VIDEO
&& !os
->parser
)
1600 av_log(s
, AV_LOG_WARNING
, "frag_type set to P-Frame reordering, but no parser found for stream %d\n", i
);
1601 os
->frag_type
= c
->streaming
? FRAG_TYPE_EVERY_FRAME
: FRAG_TYPE_NONE
;
1603 if (os
->frag_type
!= FRAG_TYPE_PFRAMES
&& as
->trick_idx
< 0)
1604 // Set this now if a parser isn't used
1605 os
->coding_dependency
= 1;
1607 if (os
->segment_type
== SEGMENT_TYPE_MP4
) {
1609 // skip_sidx : Reduce bitrate overhead
1610 // skip_trailer : Avoids growing memory usage with time
1611 av_dict_set(&opts
, "movflags", "+dash+delay_moov+skip_sidx+skip_trailer", AV_DICT_APPEND
);
1614 av_dict_set(&opts
, "movflags", "+dash+delay_moov+global_sidx+skip_trailer", AV_DICT_APPEND
);
1616 av_dict_set(&opts
, "movflags", "+dash+delay_moov+skip_trailer", AV_DICT_APPEND
);
1618 if (os
->frag_type
== FRAG_TYPE_EVERY_FRAME
)
1619 av_dict_set(&opts
, "movflags", "+frag_every_frame", AV_DICT_APPEND
);
1621 av_dict_set(&opts
, "movflags", "+frag_custom", AV_DICT_APPEND
);
1622 if (os
->frag_type
== FRAG_TYPE_DURATION
)
1623 av_dict_set_int(&opts
, "frag_duration", os
->frag_duration
, 0);
1625 av_dict_set(&opts
, "write_prft", "wallclock", 0);
1627 av_dict_set_int(&opts
, "cluster_time_limit", c
->seg_duration
/ 1000, 0);
1628 av_dict_set_int(&opts
, "cluster_size_limit", 5 * 1024 * 1024, 0); // set a large cluster size limit
1629 av_dict_set_int(&opts
, "dash", 1, 0);
1630 av_dict_set_int(&opts
, "dash_track_number", i
+ 1, 0);
1631 av_dict_set_int(&opts
, "live", 1, 0);
1633 ret
= avformat_init_output(ctx
, &opts
);
1634 av_dict_free(&opts
);
1638 avio_flush(ctx
->pb
);
1640 av_log(s
, AV_LOG_VERBOSE
, "Representation %d init segment will be written to: %s\n", i
, filename
);
1642 s
->streams
[i
]->time_base
= st
->time_base
;
1643 // If the muxer wants to shift timestamps, request to have them shifted
1644 // already before being handed to this muxer, so we don't have mismatches
1645 // between the MPD and the actual segments.
1646 s
->avoid_negative_ts
= ctx
->avoid_negative_ts
;
1647 if (st
->codecpar
->codec_type
== AVMEDIA_TYPE_VIDEO
) {
1648 AVRational avg_frame_rate
= s
->streams
[i
]->avg_frame_rate
;
1650 if (avg_frame_rate
.num
> 0) {
1651 if (av_cmp_q(avg_frame_rate
, as
->min_frame_rate
) < 0)
1652 as
->min_frame_rate
= avg_frame_rate
;
1653 if (av_cmp_q(as
->max_frame_rate
, avg_frame_rate
) < 0)
1654 as
->max_frame_rate
= avg_frame_rate
;
1656 as
->ambiguous_frame_rate
= 1;
1659 if (st
->codecpar
->width
> as
->max_width
)
1660 as
->max_width
= st
->codecpar
->width
;
1661 if (st
->codecpar
->height
> as
->max_height
)
1662 as
->max_height
= st
->codecpar
->height
;
1664 if (st
->sample_aspect_ratio
.num
)
1665 os
->sar
= st
->sample_aspect_ratio
;
1667 os
->sar
= (AVRational
){1,1};
1668 av_reduce(&par
.num
, &par
.den
,
1669 st
->codecpar
->width
* (int64_t)os
->sar
.num
,
1670 st
->codecpar
->height
* (int64_t)os
->sar
.den
,
1673 if (as
->par
.num
&& av_cmp_q(par
, as
->par
)) {
1674 av_log(s
, AV_LOG_ERROR
, "Conflicting stream par values in Adaptation Set %d\n", os
->as_idx
);
1675 return AVERROR(EINVAL
);
1682 set_codec_str(s
, st
->codecpar
, &st
->avg_frame_rate
, os
->codec_str
,
1683 sizeof(os
->codec_str
));
1684 os
->first_pts
= AV_NOPTS_VALUE
;
1685 os
->max_pts
= AV_NOPTS_VALUE
;
1686 os
->last_dts
= AV_NOPTS_VALUE
;
1687 os
->segment_index
= 1;
1689 if (s
->streams
[i
]->codecpar
->codec_type
== AVMEDIA_TYPE_VIDEO
)
1690 c
->nr_of_streams_to_flush
++;
1693 if (!c
->has_video
&& c
->seg_duration
<= 0) {
1694 av_log(s
, AV_LOG_WARNING
, "no video stream and no seg duration set\n");
1695 return AVERROR(EINVAL
);
1697 if (!c
->has_video
&& c
->frag_type
== FRAG_TYPE_PFRAMES
)
1698 av_log(s
, AV_LOG_WARNING
, "no video stream and P-frame fragmentation set\n");
1700 c
->nr_of_streams_flushed
= 0;
1701 c
->target_latency_refid
= -1;
1706 static int dash_write_header(AVFormatContext
*s
)
1708 DASHContext
*c
= s
->priv_data
;
1710 for (i
= 0; i
< s
->nb_streams
; i
++) {
1711 OutputStream
*os
= &c
->streams
[i
];
1712 if ((ret
= avformat_write_header(os
->ctx
, NULL
)) < 0)
1715 // Flush init segment
1716 // Only for WebM segment, since for mp4 delay_moov is set and
1717 // the init segment is thus flushed after the first packets.
1718 if (os
->segment_type
== SEGMENT_TYPE_WEBM
&&
1719 (ret
= flush_init_segment(s
, os
)) < 0)
1725 static int add_segment(OutputStream
*os
, const char *file
,
1726 int64_t time
, int64_t duration
,
1727 int64_t start_pos
, int64_t range_length
,
1728 int64_t index_length
, int next_exp_index
)
1732 if (os
->nb_segments
>= os
->segments_size
) {
1733 os
->segments_size
= (os
->segments_size
+ 1) * 2;
1734 if ((err
= av_reallocp_array(&os
->segments
, sizeof(*os
->segments
),
1735 os
->segments_size
)) < 0) {
1736 os
->segments_size
= 0;
1737 os
->nb_segments
= 0;
1741 seg
= av_mallocz(sizeof(*seg
));
1743 return AVERROR(ENOMEM
);
1744 av_strlcpy(seg
->file
, file
, sizeof(seg
->file
));
1746 seg
->duration
= duration
;
1747 if (seg
->time
< 0) { // If pts<0, it is expected to be cut away with an edit list
1748 seg
->duration
+= seg
->time
;
1751 seg
->start_pos
= start_pos
;
1752 seg
->range_length
= range_length
;
1753 seg
->index_length
= index_length
;
1754 os
->segments
[os
->nb_segments
++] = seg
;
1755 os
->segment_index
++;
1756 //correcting the segment index if it has fallen behind the expected value
1757 if (os
->segment_index
< next_exp_index
) {
1758 av_log(NULL
, AV_LOG_WARNING
, "Correcting the segment index after file %s: current=%d corrected=%d\n",
1759 file
, os
->segment_index
, next_exp_index
);
1760 os
->segment_index
= next_exp_index
;
1765 static void write_styp(AVIOContext
*pb
)
1768 ffio_wfourcc(pb
, "styp");
1769 ffio_wfourcc(pb
, "msdh");
1770 avio_wb32(pb
, 0); /* minor */
1771 ffio_wfourcc(pb
, "msdh");
1772 ffio_wfourcc(pb
, "msix");
1775 static void find_index_range(AVFormatContext
*s
, const char *full_path
,
1776 int64_t pos
, int *index_length
)
1782 ret
= s
->io_open(s
, &pb
, full_path
, AVIO_FLAG_READ
, NULL
);
1785 if (avio_seek(pb
, pos
, SEEK_SET
) != pos
) {
1786 ff_format_io_close(s
, &pb
);
1789 ret
= avio_read(pb
, buf
, 8);
1790 ff_format_io_close(s
, &pb
);
1793 if (AV_RL32(&buf
[4]) != MKTAG('s', 'i', 'd', 'x'))
1795 *index_length
= AV_RB32(&buf
[0]);
1798 static int update_stream_extradata(AVFormatContext
*s
, OutputStream
*os
,
1799 AVPacket
*pkt
, AVRational
*frame_rate
)
1801 AVCodecParameters
*par
= os
->ctx
->streams
[0]->codecpar
;
1803 int ret
, extradata_size
;
1805 if (par
->extradata_size
)
1808 extradata
= av_packet_get_side_data(pkt
, AV_PKT_DATA_NEW_EXTRADATA
, &extradata_size
);
1809 if (!extradata_size
)
1812 ret
= ff_alloc_extradata(par
, extradata_size
);
1816 memcpy(par
->extradata
, extradata
, extradata_size
);
1818 set_codec_str(s
, par
, frame_rate
, os
->codec_str
, sizeof(os
->codec_str
));
1823 static void dashenc_delete_file(AVFormatContext
*s
, char *filename
) {
1824 DASHContext
*c
= s
->priv_data
;
1825 int http_base_proto
= ff_is_http_proto(filename
);
1827 if (http_base_proto
) {
1828 AVIOContext
*out
= NULL
;
1829 AVDictionary
*http_opts
= NULL
;
1831 set_http_options(&http_opts
, c
);
1832 av_dict_set(&http_opts
, "method", "DELETE", 0);
1834 if (dashenc_io_open(s
, &out
, filename
, &http_opts
) < 0) {
1835 av_log(s
, AV_LOG_ERROR
, "failed to delete %s\n", filename
);
1838 av_dict_free(&http_opts
);
1839 ff_format_io_close(s
, &out
);
1841 int res
= avpriv_io_delete(filename
);
1843 char errbuf
[AV_ERROR_MAX_STRING_SIZE
];
1844 av_strerror(res
, errbuf
, sizeof(errbuf
));
1845 av_log(s
, (res
== AVERROR(ENOENT
) ? AV_LOG_WARNING
: AV_LOG_ERROR
), "failed to delete %s: %s\n", filename
, errbuf
);
1850 static int dashenc_delete_segment_file(AVFormatContext
*s
, const char* file
)
1852 DASHContext
*c
= s
->priv_data
;
1855 av_bprint_init(&buf
, 0, AV_BPRINT_SIZE_UNLIMITED
);
1857 av_bprintf(&buf
, "%s%s", c
->dirname
, file
);
1858 if (!av_bprint_is_complete(&buf
)) {
1859 av_bprint_finalize(&buf
, NULL
);
1860 av_log(s
, AV_LOG_WARNING
, "Out of memory for filename\n");
1861 return AVERROR(ENOMEM
);
1864 dashenc_delete_file(s
, buf
.str
);
1866 av_bprint_finalize(&buf
, NULL
);
1870 static inline void dashenc_delete_media_segments(AVFormatContext
*s
, OutputStream
*os
, int remove_count
)
1872 for (int i
= 0; i
< remove_count
; ++i
) {
1873 dashenc_delete_segment_file(s
, os
->segments
[i
]->file
);
1875 // Delete the segment regardless of whether the file was successfully deleted
1876 av_free(os
->segments
[i
]);
1879 os
->nb_segments
-= remove_count
;
1880 memmove(os
->segments
, os
->segments
+ remove_count
, os
->nb_segments
* sizeof(*os
->segments
));
1883 static int dash_flush(AVFormatContext
*s
, int final
, int stream
)
1885 DASHContext
*c
= s
->priv_data
;
1888 const char *proto
= avio_find_protocol_name(s
->url
);
1889 int use_rename
= proto
&& !strcmp(proto
, "file");
1891 int cur_flush_segment_index
= 0, next_exp_index
= -1;
1893 cur_flush_segment_index
= c
->streams
[stream
].segment_index
;
1895 //finding the next segment's expected index, based on the current pts value
1896 if (c
->use_template
&& !c
->use_timeline
&& c
->index_correction
&&
1897 c
->streams
[stream
].last_pts
!= AV_NOPTS_VALUE
&&
1898 c
->streams
[stream
].first_pts
!= AV_NOPTS_VALUE
) {
1899 int64_t pts_diff
= av_rescale_q(c
->streams
[stream
].last_pts
-
1900 c
->streams
[stream
].first_pts
,
1901 s
->streams
[stream
]->time_base
,
1903 next_exp_index
= (pts_diff
/ c
->streams
[stream
].seg_duration
) + 1;
1907 for (i
= 0; i
< s
->nb_streams
; i
++) {
1908 OutputStream
*os
= &c
->streams
[i
];
1909 AVStream
*st
= s
->streams
[i
];
1910 int range_length
, index_length
= 0;
1913 if (!os
->packets_written
)
1916 // Flush the single stream that got a keyframe right now.
1917 // Flush all audio streams as well, in sync with video keyframes,
1918 // but not the other video streams.
1919 if (stream
>= 0 && i
!= stream
) {
1920 if (s
->streams
[stream
]->codecpar
->codec_type
!= AVMEDIA_TYPE_VIDEO
&&
1921 s
->streams
[i
]->codecpar
->codec_type
!= AVMEDIA_TYPE_VIDEO
)
1923 if (s
->streams
[i
]->codecpar
->codec_type
!= AVMEDIA_TYPE_AUDIO
)
1925 // Make sure we don't flush audio streams multiple times, when
1926 // all video streams are flushed one at a time.
1927 if (c
->has_video
&& os
->segment_index
> cur_flush_segment_index
)
1932 snprintf(os
->full_path
, sizeof(os
->full_path
), "%s%s", c
->dirname
, os
->initfile
);
1934 ret
= flush_dynbuf(c
, os
, &range_length
);
1937 os
->packets_written
= 0;
1939 if (c
->single_file
) {
1940 find_index_range(s
, os
->full_path
, os
->pos
, &index_length
);
1942 dashenc_io_close(s
, &os
->out
, os
->temp_path
);
1945 ret
= ff_rename(os
->temp_path
, os
->full_path
, os
->ctx
);
1951 duration
= av_rescale_q(os
->max_pts
- os
->start_pts
, st
->time_base
, AV_TIME_BASE_Q
);
1952 os
->last_duration
= FFMAX(os
->last_duration
, duration
);
1954 if (!os
->muxer_overhead
&& os
->max_pts
> os
->start_pts
)
1955 os
->muxer_overhead
= ((int64_t) (range_length
- os
->total_pkt_size
) *
1956 8 * AV_TIME_BASE
) / duration
;
1957 os
->total_pkt_size
= 0;
1958 os
->total_pkt_duration
= 0;
1960 if (!os
->bit_rate
) {
1961 // calculate average bitrate of first segment
1962 int64_t bitrate
= (int64_t) range_length
* 8 * (c
->use_timeline
? os
->ctx
->streams
[0]->time_base
.den
: AV_TIME_BASE
) / duration
;
1964 os
->bit_rate
= bitrate
;
1966 add_segment(os
, os
->filename
, os
->start_pts
, os
->max_pts
- os
->start_pts
, os
->pos
, range_length
, index_length
, next_exp_index
);
1967 av_log(s
, AV_LOG_VERBOSE
, "Representation %d media segment %d written to: %s\n", i
, os
->segment_index
, os
->full_path
);
1969 os
->pos
+= range_length
;
1972 if (c
->window_size
) {
1973 for (i
= 0; i
< s
->nb_streams
; i
++) {
1974 OutputStream
*os
= &c
->streams
[i
];
1975 int remove_count
= os
->nb_segments
- c
->window_size
- c
->extra_window_size
;
1976 if (remove_count
> 0)
1977 dashenc_delete_media_segments(s
, os
, remove_count
);
1982 for (i
= 0; i
< s
->nb_streams
; i
++) {
1983 OutputStream
*os
= &c
->streams
[i
];
1984 if (os
->ctx
&& os
->ctx_inited
) {
1985 int64_t file_size
= avio_tell(os
->ctx
->pb
);
1986 av_write_trailer(os
->ctx
);
1987 if (c
->global_sidx
) {
1988 int j
, start_index
, start_number
;
1989 int64_t sidx_size
= avio_tell(os
->ctx
->pb
) - file_size
;
1990 get_start_index_number(os
, c
, &start_index
, &start_number
);
1991 if (start_index
>= os
->nb_segments
||
1992 os
->segment_type
!= SEGMENT_TYPE_MP4
)
1994 os
->init_range_length
+= sidx_size
;
1995 for (j
= start_index
; j
< os
->nb_segments
; j
++) {
1996 Segment
*seg
= os
->segments
[j
];
1997 seg
->start_pos
+= sidx_size
;
2005 if (c
->has_video
&& !final
) {
2006 c
->nr_of_streams_flushed
++;
2007 if (c
->nr_of_streams_flushed
!= c
->nr_of_streams_to_flush
)
2010 c
->nr_of_streams_flushed
= 0;
2012 ret
= write_manifest(s
, final
);
2017 static int dash_parse_prft(DASHContext
*c
, AVPacket
*pkt
)
2019 OutputStream
*os
= &c
->streams
[pkt
->stream_index
];
2020 AVProducerReferenceTime
*prft
;
2023 prft
= (AVProducerReferenceTime
*)av_packet_get_side_data(pkt
, AV_PKT_DATA_PRFT
, &side_data_size
);
2024 if (!prft
|| side_data_size
!= sizeof(AVProducerReferenceTime
) || (prft
->flags
&& prft
->flags
!= 24)) {
2025 // No encoder generated or user provided capture time AVProducerReferenceTime side data. Instead
2026 // of letting the mov muxer generate one, do it here so we can also use it for the manifest.
2027 prft
= (AVProducerReferenceTime
*)av_packet_new_side_data(pkt
, AV_PKT_DATA_PRFT
,
2028 sizeof(AVProducerReferenceTime
));
2030 return AVERROR(ENOMEM
);
2031 prft
->wallclock
= av_gettime();
2034 if (os
->first_pts
== AV_NOPTS_VALUE
) {
2035 os
->producer_reference_time
= *prft
;
2036 if (c
->target_latency_refid
< 0)
2037 c
->target_latency_refid
= pkt
->stream_index
;
2043 static int dash_write_packet(AVFormatContext
*s
, AVPacket
*pkt
)
2045 DASHContext
*c
= s
->priv_data
;
2046 AVStream
*st
= s
->streams
[pkt
->stream_index
];
2047 OutputStream
*os
= &c
->streams
[pkt
->stream_index
];
2048 AdaptationSet
*as
= &c
->as
[os
->as_idx
- 1];
2049 int64_t seg_end_duration
, elapsed_duration
;
2052 ret
= update_stream_extradata(s
, os
, pkt
, &st
->avg_frame_rate
);
2056 // Fill in a heuristic guess of the packet duration, if none is available.
2057 // The mp4 muxer will do something similar (for the last packet in a fragment)
2058 // if nothing is set (setting it for the other packets doesn't hurt).
2059 // By setting a nonzero duration here, we can be sure that the mp4 muxer won't
2060 // invoke its heuristic (this doesn't have to be identical to that algorithm),
2061 // so that we know the exact timestamps of fragments.
2062 if (!pkt
->duration
&& os
->last_dts
!= AV_NOPTS_VALUE
)
2063 pkt
->duration
= pkt
->dts
- os
->last_dts
;
2064 os
->last_dts
= pkt
->dts
;
2066 // If forcing the stream to start at 0, the mp4 muxer will set the start
2067 // timestamps to 0. Do the same here, to avoid mismatches in duration/timestamps.
2068 if (os
->first_pts
== AV_NOPTS_VALUE
&&
2069 s
->avoid_negative_ts
== AVFMT_AVOID_NEG_TS_MAKE_ZERO
) {
2070 pkt
->pts
-= pkt
->dts
;
2074 if (c
->write_prft
) {
2075 ret
= dash_parse_prft(c
, pkt
);
2080 if (os
->first_pts
== AV_NOPTS_VALUE
) {
2081 os
->first_pts
= pkt
->pts
;
2083 os
->last_pts
= pkt
->pts
;
2085 if (!c
->availability_start_time
[0]) {
2086 int64_t start_time_us
= av_gettime();
2087 c
->start_time_s
= start_time_us
/ 1000000;
2088 format_date(c
->availability_start_time
,
2089 sizeof(c
->availability_start_time
), start_time_us
);
2092 if (!os
->packets_written
)
2093 os
->availability_time_offset
= 0;
2095 if (!os
->availability_time_offset
&&
2096 ((os
->frag_type
== FRAG_TYPE_DURATION
&& os
->seg_duration
!= os
->frag_duration
) ||
2097 (os
->frag_type
== FRAG_TYPE_EVERY_FRAME
&& pkt
->duration
))) {
2098 AdaptationSet
*as
= &c
->as
[os
->as_idx
- 1];
2099 int64_t frame_duration
= 0;
2101 switch (os
->frag_type
) {
2102 case FRAG_TYPE_DURATION
:
2103 frame_duration
= os
->frag_duration
;
2105 case FRAG_TYPE_EVERY_FRAME
:
2106 frame_duration
= av_rescale_q(pkt
->duration
, st
->time_base
, AV_TIME_BASE_Q
);
2110 os
->availability_time_offset
= ((double) os
->seg_duration
-
2111 frame_duration
) / AV_TIME_BASE
;
2112 as
->max_frag_duration
= FFMAX(frame_duration
, as
->max_frag_duration
);
2115 if (c
->use_template
&& !c
->use_timeline
) {
2116 elapsed_duration
= pkt
->pts
- os
->first_pts
;
2117 seg_end_duration
= (int64_t) os
->segment_index
* os
->seg_duration
;
2119 elapsed_duration
= pkt
->pts
- os
->start_pts
;
2120 seg_end_duration
= os
->seg_duration
;
2124 (os
->frag_type
== FRAG_TYPE_PFRAMES
||
2125 as
->trick_idx
>= 0)) {
2126 // Parse the packets only in scenarios where it's needed
2129 av_parser_parse2(os
->parser
, os
->parser_avctx
,
2130 &data
, &size
, pkt
->data
, pkt
->size
,
2131 pkt
->pts
, pkt
->dts
, pkt
->pos
);
2133 os
->coding_dependency
|= os
->parser
->pict_type
!= AV_PICTURE_TYPE_I
;
2136 if (pkt
->flags
& AV_PKT_FLAG_KEY
&& os
->packets_written
&&
2137 av_compare_ts(elapsed_duration
, st
->time_base
,
2138 seg_end_duration
, AV_TIME_BASE_Q
) >= 0) {
2139 if (!c
->has_video
|| st
->codecpar
->codec_type
== AVMEDIA_TYPE_VIDEO
) {
2140 c
->last_duration
= av_rescale_q(pkt
->pts
- os
->start_pts
,
2143 c
->total_duration
= av_rescale_q(pkt
->pts
- os
->first_pts
,
2147 if ((!c
->use_timeline
|| !c
->use_template
) && os
->last_duration
) {
2148 if (c
->last_duration
< os
->last_duration
*9/10 ||
2149 c
->last_duration
> os
->last_duration
*11/10) {
2150 av_log(s
, AV_LOG_WARNING
,
2151 "Segment durations differ too much, enable use_timeline "
2152 "and use_template, or keep a stricter keyframe interval\n");
2157 if (c
->write_prft
&& os
->producer_reference_time
.wallclock
&& !os
->producer_reference_time_str
[0])
2158 format_date(os
->producer_reference_time_str
,
2159 sizeof(os
->producer_reference_time_str
),
2160 os
->producer_reference_time
.wallclock
);
2162 if ((ret
= dash_flush(s
, 0, pkt
->stream_index
)) < 0)
2166 if (!os
->packets_written
) {
2167 // If we wrote a previous segment, adjust the start time of the segment
2168 // to the end of the previous one (which is the same as the mp4 muxer
2169 // does). This avoids gaps in the timeline.
2170 if (os
->max_pts
!= AV_NOPTS_VALUE
)
2171 os
->start_pts
= os
->max_pts
;
2173 os
->start_pts
= pkt
->pts
;
2175 if (os
->max_pts
== AV_NOPTS_VALUE
)
2176 os
->max_pts
= pkt
->pts
+ pkt
->duration
;
2178 os
->max_pts
= FFMAX(os
->max_pts
, pkt
->pts
+ pkt
->duration
);
2180 if (st
->codecpar
->codec_type
== AVMEDIA_TYPE_VIDEO
&&
2181 os
->frag_type
== FRAG_TYPE_PFRAMES
&&
2182 os
->packets_written
) {
2183 av_assert0(os
->parser
);
2184 if ((os
->parser
->pict_type
== AV_PICTURE_TYPE_P
&&
2185 st
->codecpar
->video_delay
&&
2186 !(os
->last_flags
& AV_PKT_FLAG_KEY
)) ||
2187 pkt
->flags
& AV_PKT_FLAG_KEY
) {
2188 ret
= av_write_frame(os
->ctx
, NULL
);
2192 if (!os
->availability_time_offset
) {
2193 int64_t frag_duration
= av_rescale_q(os
->total_pkt_duration
, st
->time_base
,
2195 os
->availability_time_offset
= ((double) os
->seg_duration
-
2196 frag_duration
) / AV_TIME_BASE
;
2197 as
->max_frag_duration
= FFMAX(frag_duration
, as
->max_frag_duration
);
2202 if (pkt
->flags
& AV_PKT_FLAG_KEY
&& (os
->packets_written
|| os
->nb_segments
) && !os
->gop_size
&& as
->trick_idx
< 0) {
2203 os
->gop_size
= os
->last_duration
+ av_rescale_q(os
->total_pkt_duration
, st
->time_base
, AV_TIME_BASE_Q
);
2204 c
->max_gop_size
= FFMAX(c
->max_gop_size
, os
->gop_size
);
2207 if ((ret
= ff_write_chained(os
->ctx
, 0, pkt
, s
, 0)) < 0)
2210 os
->packets_written
++;
2211 os
->total_pkt_size
+= pkt
->size
;
2212 os
->total_pkt_duration
+= pkt
->duration
;
2213 os
->last_flags
= pkt
->flags
;
2215 if (!os
->init_range_length
)
2216 flush_init_segment(s
, os
);
2218 //open the output context when the first frame of a segment is ready
2219 if (!c
->single_file
&& os
->packets_written
== 1) {
2220 AVDictionary
*opts
= NULL
;
2221 const char *proto
= avio_find_protocol_name(s
->url
);
2222 int use_rename
= proto
&& !strcmp(proto
, "file");
2223 if (os
->segment_type
== SEGMENT_TYPE_MP4
)
2224 write_styp(os
->ctx
->pb
);
2225 os
->filename
[0] = os
->full_path
[0] = os
->temp_path
[0] = '\0';
2226 ff_dash_fill_tmpl_params(os
->filename
, sizeof(os
->filename
),
2227 os
->media_seg_name
, pkt
->stream_index
,
2228 os
->segment_index
, os
->bit_rate
, os
->start_pts
);
2229 snprintf(os
->full_path
, sizeof(os
->full_path
), "%s%s", c
->dirname
,
2231 snprintf(os
->temp_path
, sizeof(os
->temp_path
),
2232 use_rename
? "%s.tmp" : "%s", os
->full_path
);
2233 set_http_options(&opts
, c
);
2234 ret
= dashenc_io_open(s
, &os
->out
, os
->temp_path
, &opts
);
2235 av_dict_free(&opts
);
2237 return handle_io_open_error(s
, ret
, os
->temp_path
);
2240 char *prefetch_url
= use_rename
? NULL
: os
->filename
;
2241 write_hls_media_playlist(os
, s
, pkt
->stream_index
, 0, prefetch_url
);
2245 //write out the data immediately in streaming mode
2246 if (c
->streaming
&& os
->segment_type
== SEGMENT_TYPE_MP4
) {
2248 uint8_t *buf
= NULL
;
2249 avio_flush(os
->ctx
->pb
);
2250 len
= avio_get_dyn_buf (os
->ctx
->pb
, &buf
);
2252 avio_write(os
->out
, buf
+ os
->written_len
, len
- os
->written_len
);
2253 avio_flush(os
->out
);
2255 os
->written_len
= len
;
2261 static int dash_write_trailer(AVFormatContext
*s
)
2263 DASHContext
*c
= s
->priv_data
;
2266 if (s
->nb_streams
> 0) {
2267 OutputStream
*os
= &c
->streams
[0];
2268 // If no segments have been written so far, try to do a crude
2269 // guess of the segment duration
2270 if (!c
->last_duration
)
2271 c
->last_duration
= av_rescale_q(os
->max_pts
- os
->start_pts
,
2272 s
->streams
[0]->time_base
,
2274 c
->total_duration
= av_rescale_q(os
->max_pts
- os
->first_pts
,
2275 s
->streams
[0]->time_base
,
2278 dash_flush(s
, 1, -1);
2280 if (c
->remove_at_exit
) {
2281 for (i
= 0; i
< s
->nb_streams
; ++i
) {
2282 OutputStream
*os
= &c
->streams
[i
];
2283 dashenc_delete_media_segments(s
, os
, os
->nb_segments
);
2284 dashenc_delete_segment_file(s
, os
->initfile
);
2285 if (c
->hls_playlist
&& os
->segment_type
== SEGMENT_TYPE_MP4
) {
2286 char filename
[1024];
2287 get_hls_playlist_name(filename
, sizeof(filename
), c
->dirname
, i
);
2288 dashenc_delete_file(s
, filename
);
2291 dashenc_delete_file(s
, s
->url
);
2293 if (c
->hls_playlist
&& c
->master_playlist_created
) {
2294 char filename
[1024];
2295 snprintf(filename
, sizeof(filename
), "%smaster.m3u8", c
->dirname
);
2296 dashenc_delete_file(s
, filename
);
2303 static int dash_check_bitstream(struct AVFormatContext
*s
, const AVPacket
*avpkt
)
2305 DASHContext
*c
= s
->priv_data
;
2306 OutputStream
*os
= &c
->streams
[avpkt
->stream_index
];
2307 AVFormatContext
*oc
= os
->ctx
;
2308 if (oc
->oformat
->check_bitstream
) {
2310 AVPacket pkt
= *avpkt
;
2311 pkt
.stream_index
= 0;
2312 ret
= oc
->oformat
->check_bitstream(oc
, &pkt
);
2314 AVStream
*st
= s
->streams
[avpkt
->stream_index
];
2315 AVStream
*ost
= oc
->streams
[0];
2316 st
->internal
->bsfc
= ost
->internal
->bsfc
;
2317 ost
->internal
->bsfc
= NULL
;
2324 #define OFFSET(x) offsetof(DASHContext, x)
2325 #define E AV_OPT_FLAG_ENCODING_PARAM
2326 static const AVOption options
[] = {
2327 { "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
},
2328 { "window_size", "number of segments kept in the manifest", OFFSET(window_size
), AV_OPT_TYPE_INT
, { .i64
= 0 }, 0, INT_MAX
, E
},
2329 { "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
},
2330 #if FF_API_DASH_MIN_SEG_DURATION
2331 { "min_seg_duration", "minimum segment duration (in microseconds) (will be deprecated)", OFFSET(min_seg_duration
), AV_OPT_TYPE_INT
, { .i64
= 5000000 }, 0, INT_MAX
, E
},
2333 { "seg_duration", "segment duration (in seconds, fractional value can be set)", OFFSET(seg_duration
), AV_OPT_TYPE_DURATION
, { .i64
= 5000000 }, 0, INT_MAX
, E
},
2334 { "frag_duration", "fragment duration (in seconds, fractional value can be set)", OFFSET(frag_duration
), AV_OPT_TYPE_DURATION
, { .i64
= 0 }, 0, INT_MAX
, E
},
2335 { "frag_type", "set type of interval for fragments", OFFSET(frag_type
), AV_OPT_TYPE_INT
, {.i64
= FRAG_TYPE_NONE
}, 0, FRAG_TYPE_NB
- 1, E
, "frag_type"},
2336 { "none", "one fragment per segment", 0, AV_OPT_TYPE_CONST
, {.i64
= FRAG_TYPE_NONE
}, 0, UINT_MAX
, E
, "frag_type"},
2337 { "every_frame", "fragment at every frame", 0, AV_OPT_TYPE_CONST
, {.i64
= FRAG_TYPE_EVERY_FRAME
}, 0, UINT_MAX
, E
, "frag_type"},
2338 { "duration", "fragment at specific time intervals", 0, AV_OPT_TYPE_CONST
, {.i64
= FRAG_TYPE_DURATION
}, 0, UINT_MAX
, E
, "frag_type"},
2339 { "pframes", "fragment at keyframes and following P-Frame reordering (Video only, experimental)", 0, AV_OPT_TYPE_CONST
, {.i64
= FRAG_TYPE_PFRAMES
}, 0, UINT_MAX
, E
, "frag_type"},
2340 { "remove_at_exit", "remove all segments when finished", OFFSET(remove_at_exit
), AV_OPT_TYPE_BOOL
, { .i64
= 0 }, 0, 1, E
},
2341 { "use_template", "Use SegmentTemplate instead of SegmentList", OFFSET(use_template
), AV_OPT_TYPE_BOOL
, { .i64
= 1 }, 0, 1, E
},
2342 { "use_timeline", "Use SegmentTimeline in SegmentTemplate", OFFSET(use_timeline
), AV_OPT_TYPE_BOOL
, { .i64
= 1 }, 0, 1, E
},
2343 { "single_file", "Store all segments in one file, accessed using byte ranges", OFFSET(single_file
), AV_OPT_TYPE_BOOL
, { .i64
= 0 }, 0, 1, E
},
2344 { "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
},
2345 { "init_seg_name", "DASH-templated name to used for the initialization segment", OFFSET(init_seg_name
), AV_OPT_TYPE_STRING
, {.str
= "init-stream$RepresentationID$.$ext$"}, 0, 0, E
},
2346 { "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$.$ext$"}, 0, 0, E
},
2347 { "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, E
},
2348 { "method", "set the HTTP method", OFFSET(method
), AV_OPT_TYPE_STRING
, {.str
= NULL
}, 0, 0, E
},
2349 { "http_user_agent", "override User-Agent field in HTTP header", OFFSET(user_agent
), AV_OPT_TYPE_STRING
, {.str
= NULL
}, 0, 0, E
},
2350 { "http_persistent", "Use persistent HTTP connections", OFFSET(http_persistent
), AV_OPT_TYPE_BOOL
, {.i64
= 0 }, 0, 1, E
},
2351 { "hls_playlist", "Generate HLS playlist files(master.m3u8, media_%d.m3u8)", OFFSET(hls_playlist
), AV_OPT_TYPE_BOOL
, { .i64
= 0 }, 0, 1, E
},
2352 { "streaming", "Enable/Disable streaming mode of output. Each frame will be moof fragment", OFFSET(streaming
), AV_OPT_TYPE_BOOL
, { .i64
= 0 }, 0, 1, E
},
2353 { "timeout", "set timeout for socket I/O operations", OFFSET(timeout
), AV_OPT_TYPE_DURATION
, { .i64
= -1 }, -1, INT_MAX
, .flags
= E
},
2354 { "index_correction", "Enable/Disable segment index correction logic", OFFSET(index_correction
), AV_OPT_TYPE_BOOL
, { .i64
= 0 }, 0, 1, E
},
2355 { "format_options","set list of options for the container format (mp4/webm) used for dash", OFFSET(format_options
), AV_OPT_TYPE_DICT
, {.str
= NULL
}, 0, 0, E
},
2356 { "global_sidx", "Write global SIDX atom. Applicable only for single file, mp4 output, non-streaming mode", OFFSET(global_sidx
), AV_OPT_TYPE_BOOL
, { .i64
= 0 }, 0, 1, E
},
2357 { "dash_segment_type", "set dash segment files type", OFFSET(segment_type_option
), AV_OPT_TYPE_INT
, {.i64
= SEGMENT_TYPE_AUTO
}, 0, SEGMENT_TYPE_NB
- 1, E
, "segment_type"},
2358 { "auto", "select segment file format based on codec", 0, AV_OPT_TYPE_CONST
, {.i64
= SEGMENT_TYPE_AUTO
}, 0, UINT_MAX
, E
, "segment_type"},
2359 { "mp4", "make segment file in ISOBMFF format", 0, AV_OPT_TYPE_CONST
, {.i64
= SEGMENT_TYPE_MP4
}, 0, UINT_MAX
, E
, "segment_type"},
2360 { "webm", "make segment file in WebM format", 0, AV_OPT_TYPE_CONST
, {.i64
= SEGMENT_TYPE_WEBM
}, 0, UINT_MAX
, E
, "segment_type"},
2361 { "ignore_io_errors", "Ignore IO errors during open and write. Useful for long-duration runs with network output", OFFSET(ignore_io_errors
), AV_OPT_TYPE_BOOL
, { .i64
= 0 }, 0, 1, E
},
2362 { "lhls", "Enable Low-latency HLS(Experimental). Adds #EXT-X-PREFETCH tag with current segment's URI", OFFSET(lhls
), AV_OPT_TYPE_BOOL
, { .i64
= 0 }, 0, 1, E
},
2363 { "ldash", "Enable Low-latency dash. Constrains the value of a few elements", OFFSET(ldash
), AV_OPT_TYPE_BOOL
, { .i64
= 0 }, 0, 1, E
},
2364 { "master_m3u8_publish_rate", "Publish master playlist every after this many segment intervals", OFFSET(master_publish_rate
), AV_OPT_TYPE_INT
, {.i64
= 0}, 0, UINT_MAX
, E
},
2365 { "write_prft", "Write producer reference time element", OFFSET(write_prft
), AV_OPT_TYPE_BOOL
, {.i64
= -1}, -1, 1, E
},
2366 { "mpd_profile", "Set profiles. Elements and values used in the manifest may be constrained by them", OFFSET(profile
), AV_OPT_TYPE_FLAGS
, {.i64
= MPD_PROFILE_DASH
}, 0, UINT_MAX
, E
, "mpd_profile"},
2367 { "dash", "MPEG-DASH ISO Base media file format live profile", 0, AV_OPT_TYPE_CONST
, {.i64
= MPD_PROFILE_DASH
}, 0, UINT_MAX
, E
, "mpd_profile"},
2368 { "dvb_dash", "DVB-DASH profile", 0, AV_OPT_TYPE_CONST
, {.i64
= MPD_PROFILE_DVB
}, 0, UINT_MAX
, E
, "mpd_profile"},
2369 { "http_opts", "HTTP protocol options", OFFSET(http_opts
), AV_OPT_TYPE_DICT
, { .str
= NULL
}, 0, 0, E
},
2370 { "target_latency", "Set desired target latency for Low-latency dash", OFFSET(target_latency
), AV_OPT_TYPE_DURATION
, { .i64
= 0 }, 0, INT_MAX
, E
},
2371 { "min_playback_rate", "Set desired minimum playback rate", OFFSET(min_playback_rate
), AV_OPT_TYPE_RATIONAL
, { .dbl
= 1.0 }, 0.5, 1.5, E
},
2372 { "max_playback_rate", "Set desired maximum playback rate", OFFSET(max_playback_rate
), AV_OPT_TYPE_RATIONAL
, { .dbl
= 1.0 }, 0.5, 1.5, E
},
2376 static const AVClass dash_class
= {
2377 .class_name
= "dash muxer",
2378 .item_name
= av_default_item_name
,
2380 .version
= LIBAVUTIL_VERSION_INT
,
2383 AVOutputFormat ff_dash_muxer
= {
2385 .long_name
= NULL_IF_CONFIG_SMALL("DASH Muxer"),
2386 .extensions
= "mpd",
2387 .priv_data_size
= sizeof(DASHContext
),
2388 .audio_codec
= AV_CODEC_ID_AAC
,
2389 .video_codec
= AV_CODEC_ID_H264
,
2390 .flags
= AVFMT_GLOBALHEADER
| AVFMT_NOFILE
| AVFMT_TS_NEGATIVE
,
2392 .write_header
= dash_write_header
,
2393 .write_packet
= dash_write_packet
,
2394 .write_trailer
= dash_write_trailer
,
2395 .deinit
= dash_free
,
2396 .check_bitstream
= dash_check_bitstream
,
2397 .priv_class
= &dash_class
,