2 * Dynamic Adaptive Streaming over HTTP demux
3 * Copyright (c) 2017 samsamsam@o2.pl based on HLS demux
4 * Copyright (c) 2017 Steven Liu
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
22 #include <libxml/parser.h>
24 #include "libavutil/bprint.h"
25 #include "libavutil/mem.h"
26 #include "libavutil/opt.h"
27 #include "libavutil/time.h"
28 #include "libavutil/parseutils.h"
30 #include "avio_internal.h"
35 #define INITIAL_BUFFER_SIZE 32768
44 * reference to : ISO_IEC_23009-1-DASH-2012
46 * Table: Table 17 — Semantics of SegmentTimeline element
49 /* starttime: Element or Attribute Name
50 * specifies the MPD start time, in @timescale units,
51 * the first Segment in the series starts relative to the beginning of the Period.
52 * The value of this attribute must be equal to or greater than the sum of the previous S
53 * element earliest presentation time and the sum of the contiguous Segment durations.
54 * If the value of the attribute is greater than what is expressed by the previous S element,
55 * it expresses discontinuities in the timeline.
56 * If not present then the value shall be assumed to be zero for the first S element
57 * and for the subsequent S elements, the value shall be assumed to be the sum of
58 * the previous S element's earliest presentation time and contiguous duration
59 * (i.e. previous S@starttime + @duration * (@repeat + 1)).
62 /* repeat: Element or Attribute Name
63 * specifies the repeat count of the number of following contiguous Segments with
64 * the same duration expressed by the value of @duration. This value is zero-based
65 * (e.g. a value of three means four Segments in the contiguous series).
68 /* duration: Element or Attribute Name
69 * specifies the Segment duration, in units of the value of the @timescale.
75 * Each playlist has its own demuxer. If it is currently active,
76 * it has an opened AVIOContext too, and potentially an AVPacket
77 * containing the next packet from this stream.
79 struct representation
{
83 AVFormatContext
*parent
;
91 AVStream
*assoc_stream
; /* demuxer stream associated with this representation */
94 struct fragment
**fragments
; /* VOD list of fragment for profile */
97 struct timeline
**timelines
;
101 int64_t start_number
; /* used in case when we have dynamic list of segment to know which segments are new one*/
103 int64_t fragment_duration
;
104 int64_t fragment_timescale
;
106 int64_t presentation_timeoffset
;
109 int64_t cur_seg_offset
;
110 int64_t cur_seg_size
;
111 struct fragment
*cur_seg
;
113 /* Currently active Media Initialization Section */
114 struct fragment
*init_section
;
115 uint8_t *init_sec_buf
;
116 uint32_t init_sec_buf_size
;
117 uint32_t init_sec_data_len
;
118 uint32_t init_sec_buf_read_offset
;
119 int64_t cur_timestamp
;
120 int is_restart_needed
;
123 typedef struct DASHContext
{
124 const AVClass
*class;
128 struct representation
**videos
;
130 struct representation
**audios
;
132 struct representation
**subtitles
;
134 /* MediaPresentationDescription Attribute */
135 uint64_t media_presentation_duration
;
136 uint64_t suggested_presentation_delay
;
137 uint64_t availability_start_time
;
138 uint64_t availability_end_time
;
139 uint64_t publish_time
;
140 uint64_t minimum_update_period
;
141 uint64_t time_shift_buffer_depth
;
142 uint64_t min_buffer_time
;
144 /* Period Attribute */
145 uint64_t period_duration
;
146 uint64_t period_start
;
148 /* AdaptationSet Attribute */
149 char *adaptionset_lang
;
152 AVIOInterruptCB
*interrupt_callback
;
153 char *allowed_extensions
;
154 AVDictionary
*avio_opts
;
156 char *cenc_decryption_key
;
158 /* Flags for init section*/
159 int is_init_section_common_video
;
160 int is_init_section_common_audio
;
161 int is_init_section_common_subtitle
;
165 static int ishttp(char *url
)
167 const char *proto_name
= avio_find_protocol_name(url
);
168 return proto_name
&& av_strstart(proto_name
, "http", NULL
);
171 static int aligned(int val
)
173 return ((val
+ 0x3F) >> 6) << 6;
176 static uint64_t get_current_time_in_sec(void)
178 return av_gettime() / 1000000;
181 static uint64_t get_utc_date_time_insec(AVFormatContext
*s
, const char *datetime
)
192 /* ISO-8601 date parser */
196 ret
= sscanf(datetime
, "%d-%d-%dT%d:%d:%fZ", &year
, &month
, &day
, &hour
, &minute
, &second
);
197 /* year, month, day, hour, minute, second 6 arguments */
199 av_log(s
, AV_LOG_WARNING
, "get_utc_date_time_insec get a wrong time format\n");
201 timeinfo
.tm_year
= year
- 1900;
202 timeinfo
.tm_mon
= month
- 1;
203 timeinfo
.tm_mday
= day
;
204 timeinfo
.tm_hour
= hour
;
205 timeinfo
.tm_min
= minute
;
206 timeinfo
.tm_sec
= (int)second
;
208 return av_timegm(&timeinfo
);
211 static uint32_t get_duration_insec(AVFormatContext
*s
, const char *duration
)
213 /* ISO-8601 duration parser */
221 const char *ptr
= duration
;
224 if (*ptr
== 'P' || *ptr
== 'T') {
229 if (sscanf(ptr
, "%f%c%n", &value
, &type
, &size
) != 2) {
230 av_log(s
, AV_LOG_WARNING
, "get_duration_insec get a wrong time format\n");
231 return 0; /* parser error */
235 days
= (uint32_t)value
;
238 hours
= (uint32_t)value
;
241 mins
= (uint32_t)value
;
244 secs
= (uint32_t)value
;
247 // handle invalid type
252 return ((days
* 24 + hours
) * 60 + mins
) * 60 + secs
;
255 static int64_t get_segment_start_time_based_on_timeline(struct representation
*pls
, int64_t cur_seq_no
)
257 int64_t start_time
= 0;
262 if (pls
->n_timelines
) {
263 for (i
= 0; i
< pls
->n_timelines
; i
++) {
264 if (pls
->timelines
[i
]->starttime
> 0) {
265 start_time
= pls
->timelines
[i
]->starttime
;
267 if (num
== cur_seq_no
)
270 start_time
+= pls
->timelines
[i
]->duration
;
272 if (pls
->timelines
[i
]->repeat
== -1) {
273 start_time
= pls
->timelines
[i
]->duration
* cur_seq_no
;
277 for (j
= 0; j
< pls
->timelines
[i
]->repeat
; j
++) {
279 if (num
== cur_seq_no
)
281 start_time
+= pls
->timelines
[i
]->duration
;
290 static int64_t calc_next_seg_no_from_timelines(struct representation
*pls
, int64_t cur_time
)
295 int64_t start_time
= 0;
297 for (i
= 0; i
< pls
->n_timelines
; i
++) {
298 if (pls
->timelines
[i
]->starttime
> 0) {
299 start_time
= pls
->timelines
[i
]->starttime
;
301 if (start_time
> cur_time
)
304 start_time
+= pls
->timelines
[i
]->duration
;
305 for (j
= 0; j
< pls
->timelines
[i
]->repeat
; j
++) {
307 if (start_time
> cur_time
)
309 start_time
+= pls
->timelines
[i
]->duration
;
320 static void free_fragment(struct fragment
**seg
)
325 av_freep(&(*seg
)->url
);
329 static void free_fragment_list(struct representation
*pls
)
333 for (i
= 0; i
< pls
->n_fragments
; i
++) {
334 free_fragment(&pls
->fragments
[i
]);
336 av_freep(&pls
->fragments
);
337 pls
->n_fragments
= 0;
340 static void free_timelines_list(struct representation
*pls
)
344 for (i
= 0; i
< pls
->n_timelines
; i
++) {
345 av_freep(&pls
->timelines
[i
]);
347 av_freep(&pls
->timelines
);
348 pls
->n_timelines
= 0;
351 static void free_representation(struct representation
*pls
)
353 free_fragment_list(pls
);
354 free_timelines_list(pls
);
355 free_fragment(&pls
->cur_seg
);
356 free_fragment(&pls
->init_section
);
357 av_freep(&pls
->init_sec_buf
);
358 av_freep(&pls
->pb
.pub
.buffer
);
359 ff_format_io_close(pls
->parent
, &pls
->input
);
362 avformat_close_input(&pls
->ctx
);
365 av_freep(&pls
->url_template
);
366 av_freep(&pls
->lang
);
371 static void free_video_list(DASHContext
*c
)
374 for (i
= 0; i
< c
->n_videos
; i
++) {
375 struct representation
*pls
= c
->videos
[i
];
376 free_representation(pls
);
378 av_freep(&c
->videos
);
382 static void free_audio_list(DASHContext
*c
)
385 for (i
= 0; i
< c
->n_audios
; i
++) {
386 struct representation
*pls
= c
->audios
[i
];
387 free_representation(pls
);
389 av_freep(&c
->audios
);
393 static void free_subtitle_list(DASHContext
*c
)
396 for (i
= 0; i
< c
->n_subtitles
; i
++) {
397 struct representation
*pls
= c
->subtitles
[i
];
398 free_representation(pls
);
400 av_freep(&c
->subtitles
);
404 static int open_url(AVFormatContext
*s
, AVIOContext
**pb
, const char *url
,
405 AVDictionary
**opts
, AVDictionary
*opts2
, int *is_http
)
407 DASHContext
*c
= s
->priv_data
;
408 AVDictionary
*tmp
= NULL
;
409 const char *proto_name
= NULL
;
413 if (av_strstart(url
, "crypto", NULL
)) {
414 if (url
[6] == '+' || url
[6] == ':')
415 proto_name
= avio_find_protocol_name(url
+ 7);
419 proto_name
= avio_find_protocol_name(url
);
422 return AVERROR_INVALIDDATA
;
424 proto_name_len
= strlen(proto_name
);
425 // only http(s) & file are allowed
426 if (av_strstart(proto_name
, "file", NULL
)) {
427 if (strcmp(c
->allowed_extensions
, "ALL") && !av_match_ext(url
, c
->allowed_extensions
)) {
428 av_log(s
, AV_LOG_ERROR
,
429 "Filename extension of \'%s\' is not a common multimedia extension, blocked for security reasons.\n"
430 "If you wish to override this adjust allowed_extensions, you can set it to \'ALL\' to allow all\n",
432 return AVERROR_INVALIDDATA
;
434 } else if (av_strstart(proto_name
, "http", NULL
)) {
437 return AVERROR_INVALIDDATA
;
439 if (!strncmp(proto_name
, url
, proto_name_len
) && url
[proto_name_len
] == ':')
441 else if (av_strstart(url
, "crypto", NULL
) && !strncmp(proto_name
, url
+ 7, proto_name_len
) && url
[7 + proto_name_len
] == ':')
443 else if (strcmp(proto_name
, "file") || !strncmp(url
, "file,", 5))
444 return AVERROR_INVALIDDATA
;
447 av_dict_copy(&tmp
, *opts
, 0);
448 av_dict_copy(&tmp
, opts2
, 0);
449 ret
= ffio_open_whitelist(pb
, url
, AVIO_FLAG_READ
, c
->interrupt_callback
, &tmp
, s
->protocol_whitelist
, s
->protocol_blacklist
);
451 // update cookies on http response with setcookies.
452 char *new_cookies
= NULL
;
454 if (!(s
->flags
& AVFMT_FLAG_CUSTOM_IO
))
455 av_opt_get(*pb
, "cookies", AV_OPT_SEARCH_CHILDREN
, (uint8_t**)&new_cookies
);
458 av_dict_set(opts
, "cookies", new_cookies
, AV_DICT_DONT_STRDUP_VAL
);
466 *is_http
= av_strstart(proto_name
, "http", NULL
);
471 static char *get_content_url(xmlNodePtr
*baseurl_nodes
,
475 char *rep_bandwidth_val
,
481 char *tmp_str
= av_mallocz(max_url_size
);
486 for (i
= 0; i
< n_baseurl_nodes
; ++i
) {
487 if (baseurl_nodes
[i
] &&
488 baseurl_nodes
[i
]->children
&&
489 baseurl_nodes
[i
]->children
->type
== XML_TEXT_NODE
) {
490 text
= xmlNodeGetContent(baseurl_nodes
[i
]->children
);
492 memset(tmp_str
, 0, max_url_size
);
493 ff_make_absolute_url(tmp_str
, max_url_size
, "", text
);
500 ff_make_absolute_url(tmp_str
, max_url_size
, tmp_str
, val
);
503 url
= av_strireplace(tmp_str
, "$RepresentationID$", rep_id_val
);
507 av_strlcpy(tmp_str
, url
, max_url_size
);
509 if (rep_bandwidth_val
&& tmp_str
[0] != '\0') {
510 // free any previously assigned url before reassigning
512 url
= av_strireplace(tmp_str
, "$Bandwidth$", rep_bandwidth_val
);
522 static char *get_val_from_nodes_tab(xmlNodePtr
*nodes
, const int n_nodes
, const char *attrname
)
527 for (i
= 0; i
< n_nodes
; ++i
) {
529 val
= xmlGetProp(nodes
[i
], attrname
);
538 static xmlNodePtr
find_child_node_by_name(xmlNodePtr rootnode
, const char *nodename
)
540 xmlNodePtr node
= rootnode
;
545 node
= xmlFirstElementChild(node
);
547 if (!av_strcasecmp(node
->name
, nodename
)) {
550 node
= xmlNextElementSibling(node
);
555 static enum AVMediaType
get_content_type(xmlNodePtr node
)
557 enum AVMediaType type
= AVMEDIA_TYPE_UNKNOWN
;
563 for (i
= 0; i
< 2; i
++) {
564 attr
= i
? "mimeType" : "contentType";
565 val
= xmlGetProp(node
, attr
);
567 if (av_stristr(val
, "video")) {
568 type
= AVMEDIA_TYPE_VIDEO
;
569 } else if (av_stristr(val
, "audio")) {
570 type
= AVMEDIA_TYPE_AUDIO
;
571 } else if (av_stristr(val
, "text")) {
572 type
= AVMEDIA_TYPE_SUBTITLE
;
581 static struct fragment
*get_fragment(char *range
)
583 struct fragment
*seg
= av_mallocz(sizeof(struct fragment
));
590 char *str_end_offset
;
591 char *str_offset
= av_strtok(range
, "-", &str_end_offset
);
592 seg
->url_offset
= strtoll(str_offset
, NULL
, 10);
593 seg
->size
= strtoll(str_end_offset
, NULL
, 10) - seg
->url_offset
+ 1;
599 static int parse_manifest_segmenturlnode(AVFormatContext
*s
, struct representation
*rep
,
600 xmlNodePtr fragmenturl_node
,
601 xmlNodePtr
*baseurl_nodes
,
603 char *rep_bandwidth_val
)
605 DASHContext
*c
= s
->priv_data
;
606 char *initialization_val
= NULL
;
607 char *media_val
= NULL
;
608 char *range_val
= NULL
;
609 int max_url_size
= c
? c
->max_url_size
: MAX_URL_SIZE
;
612 if (!av_strcasecmp(fragmenturl_node
->name
, "Initialization")) {
613 initialization_val
= xmlGetProp(fragmenturl_node
, "sourceURL");
614 range_val
= xmlGetProp(fragmenturl_node
, "range");
615 if (initialization_val
|| range_val
) {
616 free_fragment(&rep
->init_section
);
617 rep
->init_section
= get_fragment(range_val
);
619 if (!rep
->init_section
) {
620 xmlFree(initialization_val
);
621 return AVERROR(ENOMEM
);
623 rep
->init_section
->url
= get_content_url(baseurl_nodes
, 4,
628 xmlFree(initialization_val
);
629 if (!rep
->init_section
->url
) {
630 av_freep(&rep
->init_section
);
631 return AVERROR(ENOMEM
);
634 } else if (!av_strcasecmp(fragmenturl_node
->name
, "SegmentURL")) {
635 media_val
= xmlGetProp(fragmenturl_node
, "media");
636 range_val
= xmlGetProp(fragmenturl_node
, "mediaRange");
637 if (media_val
|| range_val
) {
638 struct fragment
*seg
= get_fragment(range_val
);
642 return AVERROR(ENOMEM
);
644 seg
->url
= get_content_url(baseurl_nodes
, 4,
652 return AVERROR(ENOMEM
);
654 err
= av_dynarray_add_nofree(&rep
->fragments
, &rep
->n_fragments
, seg
);
665 static int parse_manifest_segmenttimeline(AVFormatContext
*s
, struct representation
*rep
,
666 xmlNodePtr fragment_timeline_node
)
668 xmlAttrPtr attr
= NULL
;
672 if (!av_strcasecmp(fragment_timeline_node
->name
, "S")) {
673 struct timeline
*tml
= av_mallocz(sizeof(struct timeline
));
675 return AVERROR(ENOMEM
);
677 attr
= fragment_timeline_node
->properties
;
679 val
= xmlGetProp(fragment_timeline_node
, attr
->name
);
682 av_log(s
, AV_LOG_WARNING
, "parse_manifest_segmenttimeline attr->name = %s val is NULL\n", attr
->name
);
686 if (!av_strcasecmp(attr
->name
, "t")) {
687 tml
->starttime
= (int64_t)strtoll(val
, NULL
, 10);
688 } else if (!av_strcasecmp(attr
->name
, "r")) {
689 tml
->repeat
=(int64_t) strtoll(val
, NULL
, 10);
690 } else if (!av_strcasecmp(attr
->name
, "d")) {
691 tml
->duration
= (int64_t)strtoll(val
, NULL
, 10);
696 err
= av_dynarray_add_nofree(&rep
->timelines
, &rep
->n_timelines
, tml
);
706 static int resolve_content_path(AVFormatContext
*s
, const char *url
, int *max_url_size
, xmlNodePtr
*baseurl_nodes
, int n_baseurl_nodes
)
708 char *tmp_str
= NULL
;
710 char *mpdName
= NULL
;
711 xmlNodePtr node
= NULL
;
712 char *baseurl
= NULL
;
713 char *root_url
= NULL
;
723 int tmp_max_url_size
= strlen(url
);
725 for (i
= n_baseurl_nodes
-1; i
>= 0 ; i
--) {
726 text
= xmlNodeGetContent(baseurl_nodes
[i
]);
729 tmp_max_url_size
+= strlen(text
);
737 tmp_max_url_size
= aligned(tmp_max_url_size
);
738 text
= av_mallocz(tmp_max_url_size
+ 1);
740 updated
= AVERROR(ENOMEM
);
743 av_strlcpy(text
, url
, strlen(url
)+1);
745 while (mpdName
= av_strtok(tmp
, "/", &tmp
)) {
746 size
= strlen(mpdName
);
750 path
= av_mallocz(tmp_max_url_size
+ 2);
751 tmp_str
= av_mallocz(tmp_max_url_size
);
752 if (!tmp_str
|| !path
) {
753 updated
= AVERROR(ENOMEM
);
757 av_strlcpy (path
, url
, strlen(url
) - size
+ 1);
758 for (rootId
= n_baseurl_nodes
- 1; rootId
> 0; rootId
--) {
759 if (!(node
= baseurl_nodes
[rootId
])) {
762 text
= xmlNodeGetContent(node
);
770 node
= baseurl_nodes
[rootId
];
771 baseurl
= xmlNodeGetContent(node
);
773 size_t len
= xmlStrlen(baseurl
)+2;
774 char *tmp
= xmlRealloc(baseurl
, len
);
776 updated
= AVERROR(ENOMEM
);
781 root_url
= (av_strcasecmp(baseurl
, "")) ? baseurl
: path
;
783 xmlChar
*escaped
= xmlEncodeSpecialChars(NULL
, root_url
);
785 updated
= AVERROR(ENOMEM
);
788 xmlNodeSetContent(node
, escaped
);
793 size
= strlen(root_url
);
794 isRootHttp
= ishttp(root_url
);
796 if (size
> 0 && root_url
[size
- 1] != token
) {
797 av_strlcat(root_url
, "/", size
+ 2);
801 for (i
= 0; i
< n_baseurl_nodes
; ++i
) {
805 text
= xmlNodeGetContent(baseurl_nodes
[i
]);
806 if (text
&& !av_strstart(text
, "/", NULL
)) {
807 memset(tmp_str
, 0, strlen(tmp_str
));
808 if (!ishttp(text
) && isRootHttp
) {
809 av_strlcpy(tmp_str
, root_url
, size
+ 1);
811 start
= (text
[0] == token
);
812 if (start
&& av_stristr(tmp_str
, text
)) {
814 if (!av_strncasecmp(tmp_str
, "http://", 7)) {
816 } else if (!av_strncasecmp(tmp_str
, "https://", 8)) {
820 memset(p
+ 1, 0, strlen(p
));
822 av_strlcat(tmp_str
, text
+ start
, tmp_max_url_size
);
824 xmlChar
* escaped
= xmlEncodeSpecialChars(NULL
, tmp_str
);
826 updated
= AVERROR(ENOMEM
);
829 xmlNodeSetContent(baseurl_nodes
[i
], escaped
);
836 if (tmp_max_url_size
> *max_url_size
) {
837 *max_url_size
= tmp_max_url_size
;
846 static int parse_manifest_representation(AVFormatContext
*s
, const char *url
,
848 xmlNodePtr adaptionset_node
,
849 xmlNodePtr mpd_baseurl_node
,
850 xmlNodePtr period_baseurl_node
,
851 xmlNodePtr period_segmenttemplate_node
,
852 xmlNodePtr period_segmentlist_node
,
853 xmlNodePtr fragment_template_node
,
854 xmlNodePtr content_component_node
,
855 xmlNodePtr adaptionset_baseurl_node
,
856 xmlNodePtr adaptionset_segmentlist_node
,
857 xmlNodePtr adaptionset_supplementalproperty_node
)
860 DASHContext
*c
= s
->priv_data
;
861 struct representation
*rep
= NULL
;
862 struct fragment
*seg
= NULL
;
863 xmlNodePtr representation_segmenttemplate_node
= NULL
;
864 xmlNodePtr representation_baseurl_node
= NULL
;
865 xmlNodePtr representation_segmentlist_node
= NULL
;
866 xmlNodePtr segmentlists_tab
[3];
867 xmlNodePtr fragment_timeline_node
= NULL
;
868 xmlNodePtr fragment_templates_tab
[5];
870 xmlNodePtr baseurl_nodes
[4];
871 xmlNodePtr representation_node
= node
;
872 char *rep_bandwidth_val
;
873 enum AVMediaType type
= AVMEDIA_TYPE_UNKNOWN
;
875 // try get information from representation
876 if (type
== AVMEDIA_TYPE_UNKNOWN
)
877 type
= get_content_type(representation_node
);
878 // try get information from contentComponen
879 if (type
== AVMEDIA_TYPE_UNKNOWN
)
880 type
= get_content_type(content_component_node
);
881 // try get information from adaption set
882 if (type
== AVMEDIA_TYPE_UNKNOWN
)
883 type
= get_content_type(adaptionset_node
);
884 if (type
!= AVMEDIA_TYPE_VIDEO
&& type
!= AVMEDIA_TYPE_AUDIO
&&
885 type
!= AVMEDIA_TYPE_SUBTITLE
) {
886 av_log(s
, AV_LOG_VERBOSE
, "Parsing '%s' - skip not supported representation type\n", url
);
890 // convert selected representation to our internal struct
891 rep
= av_mallocz(sizeof(struct representation
));
893 return AVERROR(ENOMEM
);
894 if (c
->adaptionset_lang
) {
895 rep
->lang
= av_strdup(c
->adaptionset_lang
);
897 av_log(s
, AV_LOG_ERROR
, "alloc language memory failure\n");
899 return AVERROR(ENOMEM
);
903 representation_segmenttemplate_node
= find_child_node_by_name(representation_node
, "SegmentTemplate");
904 representation_baseurl_node
= find_child_node_by_name(representation_node
, "BaseURL");
905 representation_segmentlist_node
= find_child_node_by_name(representation_node
, "SegmentList");
906 rep_bandwidth_val
= xmlGetProp(representation_node
, "bandwidth");
907 val
= xmlGetProp(representation_node
, "id");
909 rep
->id
= av_strdup(val
);
915 baseurl_nodes
[0] = mpd_baseurl_node
;
916 baseurl_nodes
[1] = period_baseurl_node
;
917 baseurl_nodes
[2] = adaptionset_baseurl_node
;
918 baseurl_nodes
[3] = representation_baseurl_node
;
920 ret
= resolve_content_path(s
, url
, &c
->max_url_size
, baseurl_nodes
, 4);
921 c
->max_url_size
= aligned(c
->max_url_size
922 + (rep
->id
? strlen(rep
->id
) : 0)
923 + (rep_bandwidth_val
? strlen(rep_bandwidth_val
) : 0));
924 if (ret
== AVERROR(ENOMEM
) || ret
== 0)
926 if (representation_segmenttemplate_node
|| fragment_template_node
|| period_segmenttemplate_node
) {
927 fragment_timeline_node
= NULL
;
928 fragment_templates_tab
[0] = representation_segmenttemplate_node
;
929 fragment_templates_tab
[1] = adaptionset_segmentlist_node
;
930 fragment_templates_tab
[2] = fragment_template_node
;
931 fragment_templates_tab
[3] = period_segmenttemplate_node
;
932 fragment_templates_tab
[4] = period_segmentlist_node
;
934 val
= get_val_from_nodes_tab(fragment_templates_tab
, 4, "initialization");
936 rep
->init_section
= av_mallocz(sizeof(struct fragment
));
937 if (!rep
->init_section
) {
941 c
->max_url_size
= aligned(c
->max_url_size
+ strlen(val
));
942 rep
->init_section
->url
= get_content_url(baseurl_nodes
, 4,
943 c
->max_url_size
, rep
->id
,
944 rep_bandwidth_val
, val
);
946 if (!rep
->init_section
->url
)
948 rep
->init_section
->size
= -1;
950 val
= get_val_from_nodes_tab(fragment_templates_tab
, 4, "media");
952 c
->max_url_size
= aligned(c
->max_url_size
+ strlen(val
));
953 rep
->url_template
= get_content_url(baseurl_nodes
, 4,
954 c
->max_url_size
, rep
->id
,
955 rep_bandwidth_val
, val
);
958 val
= get_val_from_nodes_tab(fragment_templates_tab
, 4, "presentationTimeOffset");
960 rep
->presentation_timeoffset
= (int64_t) strtoll(val
, NULL
, 10);
961 av_log(s
, AV_LOG_TRACE
, "rep->presentation_timeoffset = [%"PRId64
"]\n", rep
->presentation_timeoffset
);
964 val
= get_val_from_nodes_tab(fragment_templates_tab
, 4, "duration");
966 rep
->fragment_duration
= (int64_t) strtoll(val
, NULL
, 10);
967 av_log(s
, AV_LOG_TRACE
, "rep->fragment_duration = [%"PRId64
"]\n", rep
->fragment_duration
);
970 val
= get_val_from_nodes_tab(fragment_templates_tab
, 4, "timescale");
972 rep
->fragment_timescale
= (int64_t) strtoll(val
, NULL
, 10);
973 av_log(s
, AV_LOG_TRACE
, "rep->fragment_timescale = [%"PRId64
"]\n", rep
->fragment_timescale
);
976 val
= get_val_from_nodes_tab(fragment_templates_tab
, 4, "startNumber");
978 rep
->start_number
= rep
->first_seq_no
= (int64_t) strtoll(val
, NULL
, 10);
979 av_log(s
, AV_LOG_TRACE
, "rep->first_seq_no = [%"PRId64
"]\n", rep
->first_seq_no
);
982 if (adaptionset_supplementalproperty_node
) {
983 char *scheme_id_uri
= xmlGetProp(adaptionset_supplementalproperty_node
, "schemeIdUri");
985 int is_last_segment_number
= !av_strcasecmp(scheme_id_uri
, "http://dashif.org/guidelines/last-segment-number");
986 xmlFree(scheme_id_uri
);
987 if (is_last_segment_number
) {
988 val
= xmlGetProp(adaptionset_supplementalproperty_node
, "value");
990 av_log(s
, AV_LOG_ERROR
, "Missing value attribute in adaptionset_supplementalproperty_node\n");
992 rep
->last_seq_no
= (int64_t)strtoll(val
, NULL
, 10) - 1;
999 fragment_timeline_node
= find_child_node_by_name(representation_segmenttemplate_node
, "SegmentTimeline");
1001 if (!fragment_timeline_node
)
1002 fragment_timeline_node
= find_child_node_by_name(fragment_template_node
, "SegmentTimeline");
1003 if (!fragment_timeline_node
)
1004 fragment_timeline_node
= find_child_node_by_name(adaptionset_segmentlist_node
, "SegmentTimeline");
1005 if (!fragment_timeline_node
)
1006 fragment_timeline_node
= find_child_node_by_name(period_segmentlist_node
, "SegmentTimeline");
1007 if (fragment_timeline_node
) {
1008 fragment_timeline_node
= xmlFirstElementChild(fragment_timeline_node
);
1009 while (fragment_timeline_node
) {
1010 ret
= parse_manifest_segmenttimeline(s
, rep
, fragment_timeline_node
);
1013 fragment_timeline_node
= xmlNextElementSibling(fragment_timeline_node
);
1016 } else if (representation_baseurl_node
&& !representation_segmentlist_node
) {
1017 seg
= av_mallocz(sizeof(struct fragment
));
1020 ret
= av_dynarray_add_nofree(&rep
->fragments
, &rep
->n_fragments
, seg
);
1025 seg
->url
= get_content_url(baseurl_nodes
, 4, c
->max_url_size
,
1026 rep
->id
, rep_bandwidth_val
, NULL
);
1030 } else if (representation_segmentlist_node
) {
1031 // TODO: https://www.brendanlong.com/the-structure-of-an-mpeg-dash-mpd.html
1032 // http://www-itec.uni-klu.ac.at/dash/ddash/mpdGenerator.php?fragmentlength=15&type=full
1033 xmlNodePtr fragmenturl_node
= NULL
;
1034 segmentlists_tab
[0] = representation_segmentlist_node
;
1035 segmentlists_tab
[1] = adaptionset_segmentlist_node
;
1036 segmentlists_tab
[2] = period_segmentlist_node
;
1038 val
= get_val_from_nodes_tab(segmentlists_tab
, 3, "duration");
1040 rep
->fragment_duration
= (int64_t) strtoll(val
, NULL
, 10);
1041 av_log(s
, AV_LOG_TRACE
, "rep->fragment_duration = [%"PRId64
"]\n", rep
->fragment_duration
);
1044 val
= get_val_from_nodes_tab(segmentlists_tab
, 3, "timescale");
1046 rep
->fragment_timescale
= (int64_t) strtoll(val
, NULL
, 10);
1047 av_log(s
, AV_LOG_TRACE
, "rep->fragment_timescale = [%"PRId64
"]\n", rep
->fragment_timescale
);
1050 val
= get_val_from_nodes_tab(segmentlists_tab
, 3, "startNumber");
1052 rep
->start_number
= rep
->first_seq_no
= (int64_t) strtoll(val
, NULL
, 10);
1053 av_log(s
, AV_LOG_TRACE
, "rep->first_seq_no = [%"PRId64
"]\n", rep
->first_seq_no
);
1057 fragmenturl_node
= xmlFirstElementChild(representation_segmentlist_node
);
1058 while (fragmenturl_node
) {
1059 ret
= parse_manifest_segmenturlnode(s
, rep
, fragmenturl_node
,
1060 baseurl_nodes
, rep
->id
,
1064 fragmenturl_node
= xmlNextElementSibling(fragmenturl_node
);
1067 fragment_timeline_node
= find_child_node_by_name(adaptionset_segmentlist_node
, "SegmentTimeline");
1068 if (!fragment_timeline_node
)
1069 fragment_timeline_node
= find_child_node_by_name(period_segmentlist_node
, "SegmentTimeline");
1070 if (fragment_timeline_node
) {
1071 fragment_timeline_node
= xmlFirstElementChild(fragment_timeline_node
);
1072 while (fragment_timeline_node
) {
1073 ret
= parse_manifest_segmenttimeline(s
, rep
, fragment_timeline_node
);
1076 fragment_timeline_node
= xmlNextElementSibling(fragment_timeline_node
);
1080 av_log(s
, AV_LOG_ERROR
, "Unknown format of Representation node id '%s' \n",
1081 rep
->id
? rep
->id
: "");
1085 if (rep
->fragment_duration
> 0 && !rep
->fragment_timescale
)
1086 rep
->fragment_timescale
= 1;
1087 rep
->bandwidth
= rep_bandwidth_val
? atoi(rep_bandwidth_val
) : 0;
1088 rep
->framerate
= av_make_q(0, 0);
1089 if (type
== AVMEDIA_TYPE_VIDEO
) {
1090 char *rep_framerate_val
= xmlGetProp(representation_node
, "frameRate");
1091 if (rep_framerate_val
) {
1092 ret
= av_parse_video_rate(&rep
->framerate
, rep_framerate_val
);
1094 av_log(s
, AV_LOG_VERBOSE
, "Ignoring invalid frame rate '%s'\n", rep_framerate_val
);
1095 xmlFree(rep_framerate_val
);
1100 case AVMEDIA_TYPE_VIDEO
:
1101 ret
= av_dynarray_add_nofree(&c
->videos
, &c
->n_videos
, rep
);
1103 case AVMEDIA_TYPE_AUDIO
:
1104 ret
= av_dynarray_add_nofree(&c
->audios
, &c
->n_audios
, rep
);
1106 case AVMEDIA_TYPE_SUBTITLE
:
1107 ret
= av_dynarray_add_nofree(&c
->subtitles
, &c
->n_subtitles
, rep
);
1114 if (rep_bandwidth_val
)
1115 xmlFree(rep_bandwidth_val
);
1119 ret
= AVERROR(ENOMEM
);
1121 free_representation(rep
);
1125 static int parse_manifest_adaptationset_attr(AVFormatContext
*s
, xmlNodePtr adaptionset_node
)
1127 DASHContext
*c
= s
->priv_data
;
1129 if (!adaptionset_node
) {
1130 av_log(s
, AV_LOG_WARNING
, "Cannot get AdaptionSet\n");
1131 return AVERROR(EINVAL
);
1133 c
->adaptionset_lang
= xmlGetProp(adaptionset_node
, "lang");
1138 static int parse_manifest_adaptationset(AVFormatContext
*s
, const char *url
,
1139 xmlNodePtr adaptionset_node
,
1140 xmlNodePtr mpd_baseurl_node
,
1141 xmlNodePtr period_baseurl_node
,
1142 xmlNodePtr period_segmenttemplate_node
,
1143 xmlNodePtr period_segmentlist_node
)
1146 DASHContext
*c
= s
->priv_data
;
1147 xmlNodePtr fragment_template_node
= NULL
;
1148 xmlNodePtr content_component_node
= NULL
;
1149 xmlNodePtr adaptionset_baseurl_node
= NULL
;
1150 xmlNodePtr adaptionset_segmentlist_node
= NULL
;
1151 xmlNodePtr adaptionset_supplementalproperty_node
= NULL
;
1152 xmlNodePtr node
= NULL
;
1154 ret
= parse_manifest_adaptationset_attr(s
, adaptionset_node
);
1158 node
= xmlFirstElementChild(adaptionset_node
);
1160 if (!av_strcasecmp(node
->name
, "SegmentTemplate")) {
1161 fragment_template_node
= node
;
1162 } else if (!av_strcasecmp(node
->name
, "ContentComponent")) {
1163 content_component_node
= node
;
1164 } else if (!av_strcasecmp(node
->name
, "BaseURL")) {
1165 adaptionset_baseurl_node
= node
;
1166 } else if (!av_strcasecmp(node
->name
, "SegmentList")) {
1167 adaptionset_segmentlist_node
= node
;
1168 } else if (!av_strcasecmp(node
->name
, "SupplementalProperty")) {
1169 adaptionset_supplementalproperty_node
= node
;
1170 } else if (!av_strcasecmp(node
->name
, "Representation")) {
1171 ret
= parse_manifest_representation(s
, url
, node
,
1174 period_baseurl_node
,
1175 period_segmenttemplate_node
,
1176 period_segmentlist_node
,
1177 fragment_template_node
,
1178 content_component_node
,
1179 adaptionset_baseurl_node
,
1180 adaptionset_segmentlist_node
,
1181 adaptionset_supplementalproperty_node
);
1185 node
= xmlNextElementSibling(node
);
1189 xmlFree(c
->adaptionset_lang
);
1190 c
->adaptionset_lang
= NULL
;
1194 static int parse_programinformation(AVFormatContext
*s
, xmlNodePtr node
)
1196 xmlChar
*val
= NULL
;
1198 node
= xmlFirstElementChild(node
);
1200 if (!av_strcasecmp(node
->name
, "Title")) {
1201 val
= xmlNodeGetContent(node
);
1203 av_dict_set(&s
->metadata
, "Title", val
, 0);
1205 } else if (!av_strcasecmp(node
->name
, "Source")) {
1206 val
= xmlNodeGetContent(node
);
1208 av_dict_set(&s
->metadata
, "Source", val
, 0);
1210 } else if (!av_strcasecmp(node
->name
, "Copyright")) {
1211 val
= xmlNodeGetContent(node
);
1213 av_dict_set(&s
->metadata
, "Copyright", val
, 0);
1216 node
= xmlNextElementSibling(node
);
1223 static int parse_manifest(AVFormatContext
*s
, const char *url
, AVIOContext
*in
)
1225 DASHContext
*c
= s
->priv_data
;
1229 AVDictionary
*opts
= NULL
;
1231 xmlNodePtr root_element
= NULL
;
1232 xmlNodePtr node
= NULL
;
1233 xmlNodePtr period_node
= NULL
;
1234 xmlNodePtr tmp_node
= NULL
;
1235 xmlNodePtr mpd_baseurl_node
= NULL
;
1236 xmlNodePtr period_baseurl_node
= NULL
;
1237 xmlNodePtr period_segmenttemplate_node
= NULL
;
1238 xmlNodePtr period_segmentlist_node
= NULL
;
1239 xmlNodePtr adaptionset_node
= NULL
;
1240 xmlAttrPtr attr
= NULL
;
1242 uint32_t period_duration_sec
= 0;
1243 uint32_t period_start_sec
= 0;
1248 av_dict_copy(&opts
, c
->avio_opts
, 0);
1249 ret
= ffio_open_whitelist(&in
, url
, AVIO_FLAG_READ
, c
->interrupt_callback
, &opts
, s
->protocol_whitelist
, s
->protocol_blacklist
);
1250 av_dict_free(&opts
);
1255 if (av_opt_get(in
, "location", AV_OPT_SEARCH_CHILDREN
, (uint8_t**)&c
->base_url
) < 0)
1256 c
->base_url
= av_strdup(url
);
1258 av_bprint_init(&buf
, 0, INT_MAX
); // xmlReadMemory uses integer bufsize
1260 if ((ret
= avio_read_to_bprint(in
, &buf
, SIZE_MAX
)) < 0 ||
1262 av_log(s
, AV_LOG_ERROR
, "Unable to read to manifest '%s'\n", url
);
1264 ret
= AVERROR_INVALIDDATA
;
1268 doc
= xmlReadMemory(buf
.str
, buf
.len
, c
->base_url
, NULL
, 0);
1269 root_element
= xmlDocGetRootElement(doc
);
1270 node
= root_element
;
1273 ret
= AVERROR_INVALIDDATA
;
1274 av_log(s
, AV_LOG_ERROR
, "Unable to parse '%s' - missing root node\n", url
);
1278 if (node
->type
!= XML_ELEMENT_NODE
||
1279 av_strcasecmp(node
->name
, "MPD")) {
1280 ret
= AVERROR_INVALIDDATA
;
1281 av_log(s
, AV_LOG_ERROR
, "Unable to parse '%s' - wrong root node name[%s] type[%d]\n", url
, node
->name
, (int)node
->type
);
1285 val
= xmlGetProp(node
, "type");
1287 av_log(s
, AV_LOG_ERROR
, "Unable to parse '%s' - missing type attrib\n", url
);
1288 ret
= AVERROR_INVALIDDATA
;
1291 if (!av_strcasecmp(val
, "dynamic"))
1295 attr
= node
->properties
;
1297 val
= xmlGetProp(node
, attr
->name
);
1299 if (!av_strcasecmp(attr
->name
, "availabilityStartTime")) {
1300 c
->availability_start_time
= get_utc_date_time_insec(s
, val
);
1301 av_log(s
, AV_LOG_TRACE
, "c->availability_start_time = [%"PRId64
"]\n", c
->availability_start_time
);
1302 } else if (!av_strcasecmp(attr
->name
, "availabilityEndTime")) {
1303 c
->availability_end_time
= get_utc_date_time_insec(s
, val
);
1304 av_log(s
, AV_LOG_TRACE
, "c->availability_end_time = [%"PRId64
"]\n", c
->availability_end_time
);
1305 } else if (!av_strcasecmp(attr
->name
, "publishTime")) {
1306 c
->publish_time
= get_utc_date_time_insec(s
, val
);
1307 av_log(s
, AV_LOG_TRACE
, "c->publish_time = [%"PRId64
"]\n", c
->publish_time
);
1308 } else if (!av_strcasecmp(attr
->name
, "minimumUpdatePeriod")) {
1309 c
->minimum_update_period
= get_duration_insec(s
, val
);
1310 av_log(s
, AV_LOG_TRACE
, "c->minimum_update_period = [%"PRId64
"]\n", c
->minimum_update_period
);
1311 } else if (!av_strcasecmp(attr
->name
, "timeShiftBufferDepth")) {
1312 c
->time_shift_buffer_depth
= get_duration_insec(s
, val
);
1313 av_log(s
, AV_LOG_TRACE
, "c->time_shift_buffer_depth = [%"PRId64
"]\n", c
->time_shift_buffer_depth
);
1314 } else if (!av_strcasecmp(attr
->name
, "minBufferTime")) {
1315 c
->min_buffer_time
= get_duration_insec(s
, val
);
1316 av_log(s
, AV_LOG_TRACE
, "c->min_buffer_time = [%"PRId64
"]\n", c
->min_buffer_time
);
1317 } else if (!av_strcasecmp(attr
->name
, "suggestedPresentationDelay")) {
1318 c
->suggested_presentation_delay
= get_duration_insec(s
, val
);
1319 av_log(s
, AV_LOG_TRACE
, "c->suggested_presentation_delay = [%"PRId64
"]\n", c
->suggested_presentation_delay
);
1320 } else if (!av_strcasecmp(attr
->name
, "mediaPresentationDuration")) {
1321 c
->media_presentation_duration
= get_duration_insec(s
, val
);
1322 av_log(s
, AV_LOG_TRACE
, "c->media_presentation_duration = [%"PRId64
"]\n", c
->media_presentation_duration
);
1328 tmp_node
= find_child_node_by_name(node
, "BaseURL");
1330 mpd_baseurl_node
= xmlCopyNode(tmp_node
,1);
1332 mpd_baseurl_node
= xmlNewNode(NULL
, "BaseURL");
1335 // at now we can handle only one period, with the longest duration
1336 node
= xmlFirstElementChild(node
);
1338 if (!av_strcasecmp(node
->name
, "Period")) {
1339 period_duration_sec
= 0;
1340 period_start_sec
= 0;
1341 attr
= node
->properties
;
1343 val
= xmlGetProp(node
, attr
->name
);
1344 if (!av_strcasecmp(attr
->name
, "duration")) {
1345 period_duration_sec
= get_duration_insec(s
, val
);
1346 } else if (!av_strcasecmp(attr
->name
, "start")) {
1347 period_start_sec
= get_duration_insec(s
, val
);
1352 if ((period_duration_sec
) >= (c
->period_duration
)) {
1354 c
->period_duration
= period_duration_sec
;
1355 c
->period_start
= period_start_sec
;
1356 if (c
->period_start
> 0)
1357 c
->media_presentation_duration
= c
->period_duration
;
1359 } else if (!av_strcasecmp(node
->name
, "ProgramInformation")) {
1360 parse_programinformation(s
, node
);
1362 node
= xmlNextElementSibling(node
);
1365 av_log(s
, AV_LOG_ERROR
, "Unable to parse '%s' - missing Period node\n", url
);
1366 ret
= AVERROR_INVALIDDATA
;
1370 adaptionset_node
= xmlFirstElementChild(period_node
);
1371 while (adaptionset_node
) {
1372 if (!av_strcasecmp(adaptionset_node
->name
, "BaseURL")) {
1373 period_baseurl_node
= adaptionset_node
;
1374 } else if (!av_strcasecmp(adaptionset_node
->name
, "SegmentTemplate")) {
1375 period_segmenttemplate_node
= adaptionset_node
;
1376 } else if (!av_strcasecmp(adaptionset_node
->name
, "SegmentList")) {
1377 period_segmentlist_node
= adaptionset_node
;
1378 } else if (!av_strcasecmp(adaptionset_node
->name
, "AdaptationSet")) {
1379 parse_manifest_adaptationset(s
, url
, adaptionset_node
, mpd_baseurl_node
, period_baseurl_node
, period_segmenttemplate_node
, period_segmentlist_node
);
1381 adaptionset_node
= xmlNextElementSibling(adaptionset_node
);
1384 /*free the document */
1387 xmlFreeNode(mpd_baseurl_node
);
1390 av_bprint_finalize(&buf
, NULL
);
1397 static int64_t calc_cur_seg_no(AVFormatContext
*s
, struct representation
*pls
)
1399 DASHContext
*c
= s
->priv_data
;
1401 int64_t start_time_offset
= 0;
1404 if (pls
->n_fragments
) {
1405 av_log(s
, AV_LOG_TRACE
, "in n_fragments mode\n");
1406 num
= pls
->first_seq_no
;
1407 } else if (pls
->n_timelines
) {
1408 av_log(s
, AV_LOG_TRACE
, "in n_timelines mode\n");
1409 start_time_offset
= get_segment_start_time_based_on_timeline(pls
, 0xFFFFFFFF) - 60 * pls
->fragment_timescale
; // 60 seconds before end
1410 num
= calc_next_seg_no_from_timelines(pls
, start_time_offset
);
1412 num
= pls
->first_seq_no
;
1414 num
+= pls
->first_seq_no
;
1415 } else if (pls
->fragment_duration
){
1416 av_log(s
, AV_LOG_TRACE
, "in fragment_duration mode fragment_timescale = %"PRId64
", presentation_timeoffset = %"PRId64
"\n", pls
->fragment_timescale
, pls
->presentation_timeoffset
);
1417 if (pls
->presentation_timeoffset
) {
1418 num
= pls
->first_seq_no
+ (((get_current_time_in_sec() - c
->availability_start_time
) * pls
->fragment_timescale
)-pls
->presentation_timeoffset
) / pls
->fragment_duration
- c
->min_buffer_time
;
1419 } else if (c
->publish_time
> 0 && !c
->availability_start_time
) {
1420 if (c
->min_buffer_time
) {
1421 num
= pls
->first_seq_no
+ (((c
->publish_time
+ pls
->fragment_duration
) - c
->suggested_presentation_delay
) * pls
->fragment_timescale
) / pls
->fragment_duration
- c
->min_buffer_time
;
1423 num
= pls
->first_seq_no
+ (((c
->publish_time
- c
->time_shift_buffer_depth
+ pls
->fragment_duration
) - c
->suggested_presentation_delay
) * pls
->fragment_timescale
) / pls
->fragment_duration
;
1426 num
= pls
->first_seq_no
+ (((get_current_time_in_sec() - c
->availability_start_time
) - c
->suggested_presentation_delay
) * pls
->fragment_timescale
) / pls
->fragment_duration
;
1430 num
= pls
->first_seq_no
;
1435 static int64_t calc_min_seg_no(AVFormatContext
*s
, struct representation
*pls
)
1437 DASHContext
*c
= s
->priv_data
;
1440 if (c
->is_live
&& pls
->fragment_duration
) {
1441 av_log(s
, AV_LOG_TRACE
, "in live mode\n");
1442 num
= pls
->first_seq_no
+ (((get_current_time_in_sec() - c
->availability_start_time
) - c
->time_shift_buffer_depth
) * pls
->fragment_timescale
) / pls
->fragment_duration
;
1444 num
= pls
->first_seq_no
;
1449 static int64_t calc_max_seg_no(struct representation
*pls
, DASHContext
*c
)
1453 if (pls
->n_fragments
) {
1454 num
= pls
->first_seq_no
+ pls
->n_fragments
- 1;
1455 } else if (pls
->n_timelines
) {
1457 num
= pls
->first_seq_no
+ pls
->n_timelines
- 1;
1458 for (i
= 0; i
< pls
->n_timelines
; i
++) {
1459 if (pls
->timelines
[i
]->repeat
== -1) {
1460 int length_of_each_segment
= pls
->timelines
[i
]->duration
/ pls
->fragment_timescale
;
1461 num
= c
->period_duration
/ length_of_each_segment
;
1463 num
+= pls
->timelines
[i
]->repeat
;
1466 } else if (c
->is_live
&& pls
->fragment_duration
) {
1467 num
= pls
->first_seq_no
+ (((get_current_time_in_sec() - c
->availability_start_time
)) * pls
->fragment_timescale
) / pls
->fragment_duration
;
1468 } else if (pls
->fragment_duration
) {
1469 num
= pls
->first_seq_no
+ av_rescale_rnd(1, c
->media_presentation_duration
* pls
->fragment_timescale
, pls
->fragment_duration
, AV_ROUND_UP
);
1475 static void move_timelines(struct representation
*rep_src
, struct representation
*rep_dest
, DASHContext
*c
)
1477 if (rep_dest
&& rep_src
) {
1478 free_timelines_list(rep_dest
);
1479 rep_dest
->timelines
= rep_src
->timelines
;
1480 rep_dest
->n_timelines
= rep_src
->n_timelines
;
1481 rep_dest
->first_seq_no
= rep_src
->first_seq_no
;
1482 rep_dest
->last_seq_no
= calc_max_seg_no(rep_dest
, c
);
1483 rep_src
->timelines
= NULL
;
1484 rep_src
->n_timelines
= 0;
1485 rep_dest
->cur_seq_no
= rep_src
->cur_seq_no
;
1489 static void move_segments(struct representation
*rep_src
, struct representation
*rep_dest
, DASHContext
*c
)
1491 if (rep_dest
&& rep_src
) {
1492 free_fragment_list(rep_dest
);
1493 if (rep_src
->start_number
> (rep_dest
->start_number
+ rep_dest
->n_fragments
))
1494 rep_dest
->cur_seq_no
= 0;
1496 rep_dest
->cur_seq_no
+= rep_src
->start_number
- rep_dest
->start_number
;
1497 rep_dest
->fragments
= rep_src
->fragments
;
1498 rep_dest
->n_fragments
= rep_src
->n_fragments
;
1499 rep_dest
->parent
= rep_src
->parent
;
1500 rep_dest
->last_seq_no
= calc_max_seg_no(rep_dest
, c
);
1501 rep_src
->fragments
= NULL
;
1502 rep_src
->n_fragments
= 0;
1507 static int refresh_manifest(AVFormatContext
*s
)
1510 DASHContext
*c
= s
->priv_data
;
1511 // save current context
1512 int n_videos
= c
->n_videos
;
1513 struct representation
**videos
= c
->videos
;
1514 int n_audios
= c
->n_audios
;
1515 struct representation
**audios
= c
->audios
;
1516 int n_subtitles
= c
->n_subtitles
;
1517 struct representation
**subtitles
= c
->subtitles
;
1518 char *base_url
= c
->base_url
;
1526 c
->subtitles
= NULL
;
1527 ret
= parse_manifest(s
, s
->url
, NULL
);
1531 if (c
->n_videos
!= n_videos
) {
1532 av_log(c
, AV_LOG_ERROR
,
1533 "new manifest has mismatched no. of video representations, %d -> %d\n",
1534 n_videos
, c
->n_videos
);
1535 return AVERROR_INVALIDDATA
;
1537 if (c
->n_audios
!= n_audios
) {
1538 av_log(c
, AV_LOG_ERROR
,
1539 "new manifest has mismatched no. of audio representations, %d -> %d\n",
1540 n_audios
, c
->n_audios
);
1541 return AVERROR_INVALIDDATA
;
1543 if (c
->n_subtitles
!= n_subtitles
) {
1544 av_log(c
, AV_LOG_ERROR
,
1545 "new manifest has mismatched no. of subtitles representations, %d -> %d\n",
1546 n_subtitles
, c
->n_subtitles
);
1547 return AVERROR_INVALIDDATA
;
1550 for (i
= 0; i
< n_videos
; i
++) {
1551 struct representation
*cur_video
= videos
[i
];
1552 struct representation
*ccur_video
= c
->videos
[i
];
1553 if (cur_video
->timelines
) {
1554 // calc current time
1555 int64_t currentTime
= get_segment_start_time_based_on_timeline(cur_video
, cur_video
->cur_seq_no
) / cur_video
->fragment_timescale
;
1557 ccur_video
->cur_seq_no
= calc_next_seg_no_from_timelines(ccur_video
, currentTime
* cur_video
->fragment_timescale
- 1);
1558 if (ccur_video
->cur_seq_no
>= 0) {
1559 move_timelines(ccur_video
, cur_video
, c
);
1562 if (cur_video
->fragments
) {
1563 move_segments(ccur_video
, cur_video
, c
);
1566 for (i
= 0; i
< n_audios
; i
++) {
1567 struct representation
*cur_audio
= audios
[i
];
1568 struct representation
*ccur_audio
= c
->audios
[i
];
1569 if (cur_audio
->timelines
) {
1570 // calc current time
1571 int64_t currentTime
= get_segment_start_time_based_on_timeline(cur_audio
, cur_audio
->cur_seq_no
) / cur_audio
->fragment_timescale
;
1573 ccur_audio
->cur_seq_no
= calc_next_seg_no_from_timelines(ccur_audio
, currentTime
* cur_audio
->fragment_timescale
- 1);
1574 if (ccur_audio
->cur_seq_no
>= 0) {
1575 move_timelines(ccur_audio
, cur_audio
, c
);
1578 if (cur_audio
->fragments
) {
1579 move_segments(ccur_audio
, cur_audio
, c
);
1588 c
->base_url
= base_url
;
1591 free_subtitle_list(c
);
1597 c
->n_subtitles
= n_subtitles
;
1598 c
->subtitles
= subtitles
;
1599 c
->n_audios
= n_audios
;
1601 c
->n_videos
= n_videos
;
1606 static struct fragment
*get_current_fragment(struct representation
*pls
)
1608 int64_t min_seq_no
= 0;
1609 int64_t max_seq_no
= 0;
1610 struct fragment
*seg
= NULL
;
1611 struct fragment
*seg_ptr
= NULL
;
1612 DASHContext
*c
= pls
->parent
->priv_data
;
1614 while (( !ff_check_interrupt(c
->interrupt_callback
)&& pls
->n_fragments
> 0)) {
1615 if (pls
->cur_seq_no
< pls
->n_fragments
) {
1616 seg_ptr
= pls
->fragments
[pls
->cur_seq_no
];
1617 seg
= av_mallocz(sizeof(struct fragment
));
1621 seg
->url
= av_strdup(seg_ptr
->url
);
1626 seg
->size
= seg_ptr
->size
;
1627 seg
->url_offset
= seg_ptr
->url_offset
;
1629 } else if (c
->is_live
) {
1630 refresh_manifest(pls
->parent
);
1636 min_seq_no
= calc_min_seg_no(pls
->parent
, pls
);
1637 max_seq_no
= calc_max_seg_no(pls
, c
);
1639 if (pls
->timelines
|| pls
->fragments
) {
1640 refresh_manifest(pls
->parent
);
1642 if (pls
->cur_seq_no
<= min_seq_no
) {
1643 av_log(pls
->parent
, AV_LOG_VERBOSE
, "old fragment: cur[%"PRId64
"] min[%"PRId64
"] max[%"PRId64
"]\n", (int64_t)pls
->cur_seq_no
, min_seq_no
, max_seq_no
);
1644 pls
->cur_seq_no
= calc_cur_seg_no(pls
->parent
, pls
);
1645 } else if (pls
->cur_seq_no
> max_seq_no
) {
1646 av_log(pls
->parent
, AV_LOG_VERBOSE
, "new fragment: min[%"PRId64
"] max[%"PRId64
"]\n", min_seq_no
, max_seq_no
);
1648 seg
= av_mallocz(sizeof(struct fragment
));
1652 } else if (pls
->cur_seq_no
<= pls
->last_seq_no
) {
1653 seg
= av_mallocz(sizeof(struct fragment
));
1660 if (!pls
->url_template
) {
1661 av_log(pls
->parent
, AV_LOG_ERROR
, "Cannot get fragment, missing template URL\n");
1665 tmpfilename
= av_mallocz(c
->max_url_size
);
1670 ff_dash_fill_tmpl_params(tmpfilename
, c
->max_url_size
, pls
->url_template
, 0, pls
->cur_seq_no
, 0, get_segment_start_time_based_on_timeline(pls
, pls
->cur_seq_no
));
1671 seg
->url
= av_strireplace(pls
->url_template
, pls
->url_template
, tmpfilename
);
1673 av_log(pls
->parent
, AV_LOG_WARNING
, "Unable to resolve template url '%s', try to use origin template\n", pls
->url_template
);
1674 seg
->url
= av_strdup(pls
->url_template
);
1676 av_log(pls
->parent
, AV_LOG_ERROR
, "Cannot resolve template url '%s'\n", pls
->url_template
);
1677 av_free(tmpfilename
);
1682 av_free(tmpfilename
);
1689 static int read_from_url(struct representation
*pls
, struct fragment
*seg
,
1690 uint8_t *buf
, int buf_size
)
1694 /* limit read if the fragment was only a part of a file */
1696 buf_size
= FFMIN(buf_size
, pls
->cur_seg_size
- pls
->cur_seg_offset
);
1698 ret
= avio_read(pls
->input
, buf
, buf_size
);
1700 pls
->cur_seg_offset
+= ret
;
1705 static int open_input(DASHContext
*c
, struct representation
*pls
, struct fragment
*seg
)
1707 AVDictionary
*opts
= NULL
;
1711 url
= av_mallocz(c
->max_url_size
);
1713 ret
= AVERROR(ENOMEM
);
1717 if (seg
->size
>= 0) {
1718 /* try to restrict the HTTP request to the part we want
1719 * (if this is in fact a HTTP request) */
1720 av_dict_set_int(&opts
, "offset", seg
->url_offset
, 0);
1721 av_dict_set_int(&opts
, "end_offset", seg
->url_offset
+ seg
->size
, 0);
1724 ff_make_absolute_url(url
, c
->max_url_size
, c
->base_url
, seg
->url
);
1725 av_log(pls
->parent
, AV_LOG_VERBOSE
, "DASH request for url '%s', offset %"PRId64
"\n",
1726 url
, seg
->url_offset
);
1727 ret
= open_url(pls
->parent
, &pls
->input
, url
, &c
->avio_opts
, opts
, NULL
);
1731 av_dict_free(&opts
);
1732 pls
->cur_seg_offset
= 0;
1733 pls
->cur_seg_size
= seg
->size
;
1737 static int update_init_section(struct representation
*pls
)
1739 static const int max_init_section_size
= 1024 * 1024;
1740 DASHContext
*c
= pls
->parent
->priv_data
;
1745 if (!pls
->init_section
|| pls
->init_sec_buf
)
1748 ret
= open_input(c
, pls
, pls
->init_section
);
1750 av_log(pls
->parent
, AV_LOG_WARNING
,
1751 "Failed to open an initialization section\n");
1755 if (pls
->init_section
->size
>= 0)
1756 sec_size
= pls
->init_section
->size
;
1757 else if ((urlsize
= avio_size(pls
->input
)) >= 0)
1760 sec_size
= max_init_section_size
;
1762 av_log(pls
->parent
, AV_LOG_DEBUG
,
1763 "Downloading an initialization section of size %"PRId64
"\n",
1766 sec_size
= FFMIN(sec_size
, max_init_section_size
);
1768 av_fast_malloc(&pls
->init_sec_buf
, &pls
->init_sec_buf_size
, sec_size
);
1770 ret
= read_from_url(pls
, pls
->init_section
, pls
->init_sec_buf
,
1771 pls
->init_sec_buf_size
);
1772 ff_format_io_close(pls
->parent
, &pls
->input
);
1777 pls
->init_sec_data_len
= ret
;
1778 pls
->init_sec_buf_read_offset
= 0;
1783 static int64_t seek_data(void *opaque
, int64_t offset
, int whence
)
1785 struct representation
*v
= opaque
;
1786 if (v
->n_fragments
&& !v
->init_sec_data_len
) {
1787 return avio_seek(v
->input
, offset
, whence
);
1790 return AVERROR(ENOSYS
);
1793 static int read_data(void *opaque
, uint8_t *buf
, int buf_size
)
1796 struct representation
*v
= opaque
;
1797 DASHContext
*c
= v
->parent
->priv_data
;
1801 free_fragment(&v
->cur_seg
);
1802 v
->cur_seg
= get_current_fragment(v
);
1808 /* load/update Media Initialization Section, if any */
1809 ret
= update_init_section(v
);
1813 ret
= open_input(c
, v
, v
->cur_seg
);
1815 if (ff_check_interrupt(c
->interrupt_callback
)) {
1819 av_log(v
->parent
, AV_LOG_WARNING
, "Failed to open fragment of playlist\n");
1825 if (v
->init_sec_buf_read_offset
< v
->init_sec_data_len
) {
1826 /* Push init section out first before first actual fragment */
1827 int copy_size
= FFMIN(v
->init_sec_data_len
- v
->init_sec_buf_read_offset
, buf_size
);
1828 memcpy(buf
, v
->init_sec_buf
, copy_size
);
1829 v
->init_sec_buf_read_offset
+= copy_size
;
1834 /* check the v->cur_seg, if it is null, get current and double check if the new v->cur_seg*/
1836 v
->cur_seg
= get_current_fragment(v
);
1842 ret
= read_from_url(v
, v
->cur_seg
, buf
, buf_size
);
1846 if (c
->is_live
|| v
->cur_seq_no
< v
->last_seq_no
) {
1847 if (!v
->is_restart_needed
)
1849 v
->is_restart_needed
= 1;
1856 static int nested_io_open(AVFormatContext
*s
, AVIOContext
**pb
, const char *url
,
1857 int flags
, AVDictionary
**opts
)
1859 av_log(s
, AV_LOG_ERROR
,
1860 "A DASH playlist item '%s' referred to an external file '%s'. "
1861 "Opening this file was forbidden for security reasons\n",
1863 return AVERROR(EPERM
);
1866 static void close_demux_for_component(struct representation
*pls
)
1868 /* note: the internal buffer could have changed */
1869 av_freep(&pls
->pb
.pub
.buffer
);
1870 memset(&pls
->pb
, 0x00, sizeof(pls
->pb
));
1871 pls
->ctx
->pb
= NULL
;
1872 avformat_close_input(&pls
->ctx
);
1875 static int reopen_demux_for_component(AVFormatContext
*s
, struct representation
*pls
)
1877 DASHContext
*c
= s
->priv_data
;
1878 const AVInputFormat
*in_fmt
= NULL
;
1879 AVDictionary
*in_fmt_opts
= NULL
;
1880 uint8_t *avio_ctx_buffer
= NULL
;
1884 close_demux_for_component(pls
);
1887 if (ff_check_interrupt(&s
->interrupt_callback
)) {
1892 if (!(pls
->ctx
= avformat_alloc_context())) {
1893 ret
= AVERROR(ENOMEM
);
1897 avio_ctx_buffer
= av_malloc(INITIAL_BUFFER_SIZE
);
1898 if (!avio_ctx_buffer
) {
1899 ret
= AVERROR(ENOMEM
);
1900 avformat_free_context(pls
->ctx
);
1904 ffio_init_context(&pls
->pb
, avio_ctx_buffer
, INITIAL_BUFFER_SIZE
, 0,
1905 pls
, read_data
, NULL
, c
->is_live
? NULL
: seek_data
);
1906 pls
->pb
.pub
.seekable
= 0;
1908 if ((ret
= ff_copy_whiteblacklists(pls
->ctx
, s
)) < 0)
1911 pls
->ctx
->flags
= AVFMT_FLAG_CUSTOM_IO
;
1912 pls
->ctx
->probesize
= s
->probesize
> 0 ? s
->probesize
: 1024 * 4;
1913 pls
->ctx
->max_analyze_duration
= s
->max_analyze_duration
> 0 ? s
->max_analyze_duration
: 4 * AV_TIME_BASE
;
1914 pls
->ctx
->interrupt_callback
= s
->interrupt_callback
;
1915 ret
= av_probe_input_buffer(&pls
->pb
.pub
, &in_fmt
, "", NULL
, 0, 0);
1917 av_log(s
, AV_LOG_ERROR
, "Error when loading first fragment of playlist\n");
1918 avformat_free_context(pls
->ctx
);
1923 pls
->ctx
->pb
= &pls
->pb
.pub
;
1924 pls
->ctx
->io_open
= nested_io_open
;
1926 if (c
->cenc_decryption_key
)
1927 av_dict_set(&in_fmt_opts
, "decryption_key", c
->cenc_decryption_key
, 0);
1929 // provide additional information from mpd if available
1930 ret
= avformat_open_input(&pls
->ctx
, "", in_fmt
, &in_fmt_opts
); //pls->init_section->url
1931 av_dict_free(&in_fmt_opts
);
1934 if (pls
->n_fragments
) {
1935 #if FF_API_R_FRAME_RATE
1936 if (pls
->framerate
.den
) {
1937 for (i
= 0; i
< pls
->ctx
->nb_streams
; i
++)
1938 pls
->ctx
->streams
[i
]->r_frame_rate
= pls
->framerate
;
1941 ret
= avformat_find_stream_info(pls
->ctx
, NULL
);
1950 static int open_demux_for_component(AVFormatContext
*s
, struct representation
*pls
)
1956 pls
->cur_seq_no
= calc_cur_seg_no(s
, pls
);
1958 if (!pls
->last_seq_no
)
1959 pls
->last_seq_no
= calc_max_seg_no(pls
, s
->priv_data
);
1961 ret
= reopen_demux_for_component(s
, pls
);
1965 for (i
= 0; i
< pls
->ctx
->nb_streams
; i
++) {
1966 AVStream
*st
= avformat_new_stream(s
, NULL
);
1967 AVStream
*ist
= pls
->ctx
->streams
[i
];
1969 return AVERROR(ENOMEM
);
1973 ret
= avcodec_parameters_copy(st
->codecpar
, ist
->codecpar
);
1977 avpriv_set_pts_info(st
, ist
->pts_wrap_bits
, ist
->time_base
.num
, ist
->time_base
.den
);
1980 st
->disposition
= ist
->disposition
;
1986 static int is_common_init_section_exist(struct representation
**pls
, int n_pls
)
1988 struct fragment
*first_init_section
= pls
[0]->init_section
;
1990 int64_t url_offset
= -1;
1994 if (first_init_section
== NULL
|| n_pls
== 0)
1997 url
= first_init_section
->url
;
1998 url_offset
= first_init_section
->url_offset
;
1999 size
= pls
[0]->init_section
->size
;
2000 for (i
=0;i
<n_pls
;i
++) {
2001 if (!pls
[i
]->init_section
)
2004 if (av_strcasecmp(pls
[i
]->init_section
->url
, url
) ||
2005 pls
[i
]->init_section
->url_offset
!= url_offset
||
2006 pls
[i
]->init_section
->size
!= size
) {
2013 static int copy_init_section(struct representation
*rep_dest
, struct representation
*rep_src
)
2015 rep_dest
->init_sec_buf
= av_mallocz(rep_src
->init_sec_buf_size
);
2016 if (!rep_dest
->init_sec_buf
) {
2017 av_log(rep_dest
->ctx
, AV_LOG_WARNING
, "Cannot alloc memory for init_sec_buf\n");
2018 return AVERROR(ENOMEM
);
2020 memcpy(rep_dest
->init_sec_buf
, rep_src
->init_sec_buf
, rep_src
->init_sec_data_len
);
2021 rep_dest
->init_sec_buf_size
= rep_src
->init_sec_buf_size
;
2022 rep_dest
->init_sec_data_len
= rep_src
->init_sec_data_len
;
2023 rep_dest
->cur_timestamp
= rep_src
->cur_timestamp
;
2028 static void move_metadata(AVStream
*st
, const char *key
, char **value
)
2031 av_dict_set(&st
->metadata
, key
, *value
, AV_DICT_DONT_STRDUP_VAL
);
2036 static int dash_read_header(AVFormatContext
*s
)
2038 DASHContext
*c
= s
->priv_data
;
2039 struct representation
*rep
;
2042 int stream_index
= 0;
2045 c
->interrupt_callback
= &s
->interrupt_callback
;
2047 if ((ret
= ffio_copy_url_options(s
->pb
, &c
->avio_opts
)) < 0)
2050 if ((ret
= parse_manifest(s
, s
->url
, s
->pb
)) < 0)
2053 /* If this isn't a live stream, fill the total duration of the
2056 s
->duration
= (int64_t) c
->media_presentation_duration
* AV_TIME_BASE
;
2058 av_dict_set(&c
->avio_opts
, "seekable", "0", 0);
2062 c
->is_init_section_common_video
= is_common_init_section_exist(c
->videos
, c
->n_videos
);
2064 /* Open the demuxer for video and audio components if available */
2065 for (i
= 0; i
< c
->n_videos
; i
++) {
2067 if (i
> 0 && c
->is_init_section_common_video
) {
2068 ret
= copy_init_section(rep
, c
->videos
[0]);
2072 ret
= open_demux_for_component(s
, rep
);
2076 rep
->stream_index
= stream_index
;
2081 c
->is_init_section_common_audio
= is_common_init_section_exist(c
->audios
, c
->n_audios
);
2083 for (i
= 0; i
< c
->n_audios
; i
++) {
2085 if (i
> 0 && c
->is_init_section_common_audio
) {
2086 ret
= copy_init_section(rep
, c
->audios
[0]);
2090 ret
= open_demux_for_component(s
, rep
);
2094 rep
->stream_index
= stream_index
;
2099 c
->is_init_section_common_subtitle
= is_common_init_section_exist(c
->subtitles
, c
->n_subtitles
);
2101 for (i
= 0; i
< c
->n_subtitles
; i
++) {
2102 rep
= c
->subtitles
[i
];
2103 if (i
> 0 && c
->is_init_section_common_subtitle
) {
2104 ret
= copy_init_section(rep
, c
->subtitles
[0]);
2108 ret
= open_demux_for_component(s
, rep
);
2112 rep
->stream_index
= stream_index
;
2117 return AVERROR_INVALIDDATA
;
2119 /* Create a program */
2120 program
= av_new_program(s
, 0);
2122 return AVERROR(ENOMEM
);
2124 for (i
= 0; i
< c
->n_videos
; i
++) {
2126 av_program_add_stream_index(s
, 0, rep
->stream_index
);
2127 rep
->assoc_stream
= s
->streams
[rep
->stream_index
];
2128 if (rep
->bandwidth
> 0)
2129 av_dict_set_int(&rep
->assoc_stream
->metadata
, "variant_bitrate", rep
->bandwidth
, 0);
2130 move_metadata(rep
->assoc_stream
, "id", &rep
->id
);
2132 for (i
= 0; i
< c
->n_audios
; i
++) {
2134 av_program_add_stream_index(s
, 0, rep
->stream_index
);
2135 rep
->assoc_stream
= s
->streams
[rep
->stream_index
];
2136 if (rep
->bandwidth
> 0)
2137 av_dict_set_int(&rep
->assoc_stream
->metadata
, "variant_bitrate", rep
->bandwidth
, 0);
2138 move_metadata(rep
->assoc_stream
, "id", &rep
->id
);
2139 move_metadata(rep
->assoc_stream
, "language", &rep
->lang
);
2141 for (i
= 0; i
< c
->n_subtitles
; i
++) {
2142 rep
= c
->subtitles
[i
];
2143 av_program_add_stream_index(s
, 0, rep
->stream_index
);
2144 rep
->assoc_stream
= s
->streams
[rep
->stream_index
];
2145 move_metadata(rep
->assoc_stream
, "id", &rep
->id
);
2146 move_metadata(rep
->assoc_stream
, "language", &rep
->lang
);
2152 static void recheck_discard_flags(AVFormatContext
*s
, struct representation
**p
, int n
)
2156 for (i
= 0; i
< n
; i
++) {
2157 struct representation
*pls
= p
[i
];
2158 int needed
= !pls
->assoc_stream
|| pls
->assoc_stream
->discard
< AVDISCARD_ALL
;
2160 if (needed
&& !pls
->ctx
) {
2161 pls
->cur_seg_offset
= 0;
2162 pls
->init_sec_buf_read_offset
= 0;
2164 for (j
= 0; j
< n
; j
++) {
2165 pls
->cur_seq_no
= FFMAX(pls
->cur_seq_no
, p
[j
]->cur_seq_no
);
2167 reopen_demux_for_component(s
, pls
);
2168 av_log(s
, AV_LOG_INFO
, "Now receiving stream_index %d\n", pls
->stream_index
);
2169 } else if (!needed
&& pls
->ctx
) {
2170 close_demux_for_component(pls
);
2171 ff_format_io_close(pls
->parent
, &pls
->input
);
2172 av_log(s
, AV_LOG_INFO
, "No longer receiving stream_index %d\n", pls
->stream_index
);
2177 static int dash_read_packet(AVFormatContext
*s
, AVPacket
*pkt
)
2179 DASHContext
*c
= s
->priv_data
;
2182 struct representation
*cur
= NULL
;
2183 struct representation
*rep
= NULL
;
2185 recheck_discard_flags(s
, c
->videos
, c
->n_videos
);
2186 recheck_discard_flags(s
, c
->audios
, c
->n_audios
);
2187 recheck_discard_flags(s
, c
->subtitles
, c
->n_subtitles
);
2189 for (i
= 0; i
< c
->n_videos
; i
++) {
2193 if (!cur
|| rep
->cur_timestamp
< mints
) {
2195 mints
= rep
->cur_timestamp
;
2198 for (i
= 0; i
< c
->n_audios
; i
++) {
2202 if (!cur
|| rep
->cur_timestamp
< mints
) {
2204 mints
= rep
->cur_timestamp
;
2208 for (i
= 0; i
< c
->n_subtitles
; i
++) {
2209 rep
= c
->subtitles
[i
];
2212 if (!cur
|| rep
->cur_timestamp
< mints
) {
2214 mints
= rep
->cur_timestamp
;
2219 return AVERROR_INVALIDDATA
;
2221 while (!ff_check_interrupt(c
->interrupt_callback
) && !ret
) {
2222 ret
= av_read_frame(cur
->ctx
, pkt
);
2224 /* If we got a packet, return it */
2225 cur
->cur_timestamp
= av_rescale(pkt
->pts
, (int64_t)cur
->ctx
->streams
[0]->time_base
.num
* 90000, cur
->ctx
->streams
[0]->time_base
.den
);
2226 pkt
->stream_index
= cur
->stream_index
;
2229 if (cur
->is_restart_needed
) {
2230 cur
->cur_seg_offset
= 0;
2231 cur
->init_sec_buf_read_offset
= 0;
2232 cur
->is_restart_needed
= 0;
2233 ff_format_io_close(cur
->parent
, &cur
->input
);
2234 ret
= reopen_demux_for_component(s
, cur
);
2240 static int dash_close(AVFormatContext
*s
)
2242 DASHContext
*c
= s
->priv_data
;
2245 free_subtitle_list(c
);
2246 av_dict_free(&c
->avio_opts
);
2247 av_freep(&c
->base_url
);
2251 static int dash_seek(AVFormatContext
*s
, struct representation
*pls
, int64_t seek_pos_msec
, int flags
, int dry_run
)
2256 int64_t duration
= 0;
2258 av_log(pls
->parent
, AV_LOG_VERBOSE
, "DASH seek pos[%"PRId64
"ms] %s\n",
2259 seek_pos_msec
, dry_run
? " (dry)" : "");
2261 // single fragment mode
2262 if (pls
->n_fragments
== 1) {
2263 pls
->cur_timestamp
= 0;
2264 pls
->cur_seg_offset
= 0;
2267 ff_read_frame_flush(pls
->ctx
);
2268 return av_seek_frame(pls
->ctx
, -1, seek_pos_msec
* 1000, flags
);
2271 ff_format_io_close(pls
->parent
, &pls
->input
);
2273 // find the nearest fragment
2274 if (pls
->n_timelines
> 0 && pls
->fragment_timescale
> 0) {
2275 int64_t num
= pls
->first_seq_no
;
2276 av_log(pls
->parent
, AV_LOG_VERBOSE
, "dash_seek with SegmentTimeline start n_timelines[%d] "
2277 "last_seq_no[%"PRId64
"].\n",
2278 (int)pls
->n_timelines
, (int64_t)pls
->last_seq_no
);
2279 for (i
= 0; i
< pls
->n_timelines
; i
++) {
2280 if (pls
->timelines
[i
]->starttime
> 0) {
2281 duration
= pls
->timelines
[i
]->starttime
;
2283 duration
+= pls
->timelines
[i
]->duration
;
2284 if (seek_pos_msec
< ((duration
* 1000) / pls
->fragment_timescale
)) {
2287 for (j
= 0; j
< pls
->timelines
[i
]->repeat
; j
++) {
2288 duration
+= pls
->timelines
[i
]->duration
;
2290 if (seek_pos_msec
< ((duration
* 1000) / pls
->fragment_timescale
)) {
2298 pls
->cur_seq_no
= num
> pls
->last_seq_no
? pls
->last_seq_no
: num
;
2299 av_log(pls
->parent
, AV_LOG_VERBOSE
, "dash_seek with SegmentTimeline end cur_seq_no[%"PRId64
"].\n",
2300 (int64_t)pls
->cur_seq_no
);
2301 } else if (pls
->fragment_duration
> 0) {
2302 pls
->cur_seq_no
= pls
->first_seq_no
+ ((seek_pos_msec
* pls
->fragment_timescale
) / pls
->fragment_duration
) / 1000;
2304 av_log(pls
->parent
, AV_LOG_ERROR
, "dash_seek missing timeline or fragment_duration\n");
2305 pls
->cur_seq_no
= pls
->first_seq_no
;
2307 pls
->cur_timestamp
= 0;
2308 pls
->cur_seg_offset
= 0;
2309 pls
->init_sec_buf_read_offset
= 0;
2310 ret
= dry_run
? 0 : reopen_demux_for_component(s
, pls
);
2315 static int dash_read_seek(AVFormatContext
*s
, int stream_index
, int64_t timestamp
, int flags
)
2318 DASHContext
*c
= s
->priv_data
;
2319 int64_t seek_pos_msec
= av_rescale_rnd(timestamp
, 1000,
2320 s
->streams
[stream_index
]->time_base
.den
,
2321 flags
& AVSEEK_FLAG_BACKWARD
?
2322 AV_ROUND_DOWN
: AV_ROUND_UP
);
2323 if ((flags
& AVSEEK_FLAG_BYTE
) || c
->is_live
)
2324 return AVERROR(ENOSYS
);
2326 /* Seek in discarded streams with dry_run=1 to avoid reopening them */
2327 for (i
= 0; i
< c
->n_videos
; i
++) {
2329 ret
= dash_seek(s
, c
->videos
[i
], seek_pos_msec
, flags
, !c
->videos
[i
]->ctx
);
2331 for (i
= 0; i
< c
->n_audios
; i
++) {
2333 ret
= dash_seek(s
, c
->audios
[i
], seek_pos_msec
, flags
, !c
->audios
[i
]->ctx
);
2335 for (i
= 0; i
< c
->n_subtitles
; i
++) {
2337 ret
= dash_seek(s
, c
->subtitles
[i
], seek_pos_msec
, flags
, !c
->subtitles
[i
]->ctx
);
2343 static int dash_probe(const AVProbeData
*p
)
2345 if (!av_stristr(p
->buf
, "<MPD"))
2348 if (av_stristr(p
->buf
, "dash:profile:isoff-on-demand:2011") ||
2349 av_stristr(p
->buf
, "dash:profile:isoff-live:2011") ||
2350 av_stristr(p
->buf
, "dash:profile:isoff-live:2012") ||
2351 av_stristr(p
->buf
, "dash:profile:isoff-main:2011") ||
2352 av_stristr(p
->buf
, "3GPP:PSS:profile:DASH1")) {
2353 return AVPROBE_SCORE_MAX
;
2355 if (av_stristr(p
->buf
, "dash:profile")) {
2356 return AVPROBE_SCORE_MAX
;
2362 #define OFFSET(x) offsetof(DASHContext, x)
2363 #define FLAGS AV_OPT_FLAG_DECODING_PARAM
2364 static const AVOption dash_options
[] = {
2365 {"allowed_extensions", "List of file extensions that dash is allowed to access",
2366 OFFSET(allowed_extensions
), AV_OPT_TYPE_STRING
,
2367 {.str
= "aac,m4a,m4s,m4v,mov,mp4,webm,ts"},
2368 INT_MIN
, INT_MAX
, FLAGS
},
2369 { "cenc_decryption_key", "Media decryption key (hex)", OFFSET(cenc_decryption_key
), AV_OPT_TYPE_STRING
, {.str
= NULL
}, INT_MIN
, INT_MAX
, .flags
= FLAGS
},
2373 static const AVClass dash_class
= {
2374 .class_name
= "dash",
2375 .item_name
= av_default_item_name
,
2376 .option
= dash_options
,
2377 .version
= LIBAVUTIL_VERSION_INT
,
2380 const FFInputFormat ff_dash_demuxer
= {
2382 .p
.long_name
= NULL_IF_CONFIG_SMALL("Dynamic Adaptive Streaming over HTTP"),
2383 .p
.priv_class
= &dash_class
,
2384 .p
.flags
= AVFMT_NO_BYTE_SEEK
,
2385 .priv_data_size
= sizeof(DASHContext
),
2386 .flags_internal
= FF_INFMT_FLAG_INIT_CLEANUP
,
2387 .read_probe
= dash_probe
,
2388 .read_header
= dash_read_header
,
2389 .read_packet
= dash_read_packet
,
2390 .read_close
= dash_close
,
2391 .read_seek
= dash_read_seek
,