]> git.sesse.net Git - vlc/blob - modules/demux/dash/mpd/IsoffMainParser.cpp
demux: dash: add missing period duration parsing
[vlc] / modules / demux / dash / mpd / IsoffMainParser.cpp
1 /*
2  * IsoffMainParser.cpp
3  *****************************************************************************
4  * Copyright (C) 2010 - 2012 Klagenfurt University
5  *
6  * Created on: Jan 27, 2012
7  * Authors: Christopher Mueller <christopher.mueller@itec.uni-klu.ac.at>
8  *          Christian Timmerer  <christian.timmerer@itec.uni-klu.ac.at>
9  *
10  * This program is free software; you can redistribute it and/or modify it
11  * under the terms of the GNU Lesser General Public License as published
12  * by the Free Software Foundation; either version 2.1 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18  * GNU Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public License
21  * along with this program; if not, write to the Free Software Foundation,
22  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23  *****************************************************************************/
24
25 #ifdef HAVE_CONFIG_H
26 # include "config.h"
27 #endif
28
29 #include "IsoffMainParser.h"
30 #include "SegmentTemplate.h"
31 #include "SegmentTimeline.h"
32 #include "ProgramInformation.h"
33 #include "xml/DOMHelper.h"
34 #include <vlc_strings.h>
35 #include <vlc_stream.h>
36 #include <cstdio>
37 #include <sstream>
38
39 using namespace dash::mpd;
40 using namespace dash::xml;
41
42 IsoffMainParser::IsoffMainParser    (Node *root, stream_t *p_stream) :
43                  IMPDParser(root, NULL, p_stream, NULL)
44 {
45 }
46 IsoffMainParser::~IsoffMainParser   ()
47 {
48 }
49
50 bool    IsoffMainParser::parse              (Profile profile)
51 {
52     mpd = new MPD(p_stream, profile);
53     setMPDAttributes();
54     parseProgramInformation(DOMHelper::getFirstChildElementByName(root, "ProgramInformation"), mpd);
55     setMPDBaseUrl(root);
56     parsePeriods(root);
57
58     print();
59     return true;
60 }
61
62 void    IsoffMainParser::setMPDAttributes   ()
63 {
64     const std::map<std::string, std::string> attr = this->root->getAttributes();
65
66     std::map<std::string, std::string>::const_iterator it;
67
68     it = attr.find("mediaPresentationDuration");
69     if(it != attr.end())
70         this->mpd->duration.Set(IsoTime(it->second));
71
72     it = attr.find("minBufferTime");
73     if(it != attr.end())
74         this->mpd->minBufferTime.Set(IsoTime(it->second));
75
76     it = attr.find("minimumUpdatePeriod");
77     if(it != attr.end())
78         mpd->minUpdatePeriod.Set(IsoTime(it->second));
79
80     it = attr.find("maxSegmentDuration");
81     if(it != attr.end())
82         mpd->maxSegmentDuration.Set(IsoTime(it->second));
83
84     it = attr.find("type");
85     if(it != attr.end())
86         mpd->setType(it->second);
87
88     it = attr.find("availabilityStartTime");
89     if(it != attr.end())
90         mpd->availabilityStartTime.Set(UTCTime(it->second));
91
92     it = attr.find("timeShiftBufferDepth");
93         if(it != attr.end())
94             mpd->timeShiftBufferDepth.Set(IsoTime(it->second));
95 }
96
97 void IsoffMainParser::parsePeriods(Node *root)
98 {
99     std::vector<Node *> periods = DOMHelper::getElementByTagName(root, "Period", false);
100     std::vector<Node *>::const_iterator it;
101
102     for(it = periods.begin(); it != periods.end(); it++)
103     {
104         Period *period = new (std::nothrow) Period(mpd);
105         if (!period)
106             continue;
107         parseSegmentInformation(*it, period);
108         if((*it)->hasAttribute("start"))
109             period->startTime.Set(IsoTime((*it)->getAttributeValue("start")));
110         if((*it)->hasAttribute("duration"))
111             period->duration.Set(IsoTime((*it)->getAttributeValue("duration")));
112         if((*it)->hasAttribute("id"))
113             period->setId((*it)->getAttributeValue("id"));
114         setAdaptationSets(*it, period);
115         mpd->addPeriod(period);
116     }
117 }
118
119 size_t IsoffMainParser::parseSegmentTemplate(Node *templateNode, SegmentInformation *info)
120 {
121     size_t total = 0;
122     if (templateNode == NULL || !templateNode->hasAttribute("media"))
123         return total;
124
125     std::string mediaurl = templateNode->getAttributeValue("media");
126     MediaSegmentTemplate *mediaTemplate = NULL;
127     if(mediaurl.empty() || !(mediaTemplate = new (std::nothrow) MediaSegmentTemplate(info)) )
128         return total;
129     mediaTemplate->setSourceUrl(mediaurl);
130
131     if(templateNode->hasAttribute("startNumber"))
132         mediaTemplate->startNumber.Set(Integer<uint64_t>(templateNode->getAttributeValue("startNumber")));
133
134     if(templateNode->hasAttribute("duration"))
135         mediaTemplate->duration.Set(Integer<mtime_t>(templateNode->getAttributeValue("duration")));
136
137     if(templateNode->hasAttribute("timescale"))
138         mediaTemplate->timescale.Set(Integer<uint64_t>(templateNode->getAttributeValue("timescale")));
139
140     InitSegmentTemplate *initTemplate = NULL;
141
142     if(templateNode->hasAttribute("initialization"))
143     {
144         std::string initurl = templateNode->getAttributeValue("initialization");
145         if(!initurl.empty() && (initTemplate = new (std::nothrow) InitSegmentTemplate(info)))
146             initTemplate->setSourceUrl(initurl);
147     }
148
149     mediaTemplate->initialisationSegment.Set(initTemplate);
150     info->setSegmentTemplate(mediaTemplate);
151
152     parseTimeline(DOMHelper::getFirstChildElementByName(templateNode, "SegmentTimeline"), mediaTemplate);
153
154     total += ( mediaTemplate != NULL );
155
156     return total;
157 }
158
159 size_t IsoffMainParser::parseSegmentInformation(Node *node, SegmentInformation *info)
160 {
161     size_t total = 0;
162     parseSegmentBase(DOMHelper::getFirstChildElementByName(node, "SegmentBase"), info);
163     total += parseSegmentList(DOMHelper::getFirstChildElementByName(node, "SegmentList"), info);
164     total += parseSegmentTemplate(DOMHelper::getFirstChildElementByName(node, "SegmentTemplate" ), info);
165     if(node->hasAttribute("bitstreamSwitching"))
166         info->setBitstreamSwitching(node->getAttributeValue("bitstreamSwitching") == "true");
167     if(node->hasAttribute("timescale"))
168         info->timescale.Set(Integer<uint64_t>(node->getAttributeValue("timescale")));
169     return total;
170 }
171
172 void    IsoffMainParser::setAdaptationSets  (Node *periodNode, Period *period)
173 {
174     std::vector<Node *> adaptationSets = DOMHelper::getElementByTagName(periodNode, "AdaptationSet", false);
175     std::vector<Node *>::const_iterator it;
176
177     for(it = adaptationSets.begin(); it != adaptationSets.end(); it++)
178     {
179         AdaptationSet *adaptationSet = new AdaptationSet(period);
180         if(!adaptationSet)
181             continue;
182         if((*it)->hasAttribute("mimeType"))
183             adaptationSet->setMimeType((*it)->getAttributeValue("mimeType"));
184
185         parseSegmentInformation( *it, adaptationSet );
186
187         setRepresentations((*it), adaptationSet);
188         period->addAdaptationSet(adaptationSet);
189     }
190 }
191 void    IsoffMainParser::setRepresentations (Node *adaptationSetNode, AdaptationSet *adaptationSet)
192 {
193     std::vector<Node *> representations = DOMHelper::getElementByTagName(adaptationSetNode, "Representation", false);
194
195     for(size_t i = 0; i < representations.size(); i++)
196     {
197         this->currentRepresentation = new Representation(adaptationSet, getMPD());
198         Node *repNode = representations.at(i);
199
200         std::vector<Node *> baseUrls = DOMHelper::getChildElementByTagName(repNode, "BaseURL");
201         if(!baseUrls.empty())
202             currentRepresentation->setBaseUrl( new BaseUrl( baseUrls.front()->getText() ) );
203
204         if(repNode->hasAttribute("id"))
205             currentRepresentation->setId(repNode->getAttributeValue("id"));
206
207         if(repNode->hasAttribute("width"))
208             this->currentRepresentation->setWidth(atoi(repNode->getAttributeValue("width").c_str()));
209
210         if(repNode->hasAttribute("height"))
211             this->currentRepresentation->setHeight(atoi(repNode->getAttributeValue("height").c_str()));
212
213         if(repNode->hasAttribute("bandwidth"))
214             this->currentRepresentation->setBandwidth(atoi(repNode->getAttributeValue("bandwidth").c_str()));
215
216         if(repNode->hasAttribute("mimeType"))
217             currentRepresentation->setMimeType(repNode->getAttributeValue("mimeType"));
218
219         size_t totalmediasegments = parseSegmentInformation(repNode, currentRepresentation);
220         if(!totalmediasegments)
221         {
222             /* unranged & segment less representation, add fake segment */
223             SegmentList *list = new SegmentList();
224             Segment *seg = new Segment(currentRepresentation);
225             if(list && seg)
226             {
227                 list->addSegment(seg);
228                 currentRepresentation->setSegmentList(list);
229             }
230             else
231             {
232                 delete seg;
233                 delete list;
234             }
235         }
236
237         adaptationSet->addRepresentation(this->currentRepresentation);
238     }
239 }
240
241 void IsoffMainParser::parseSegmentBase(Node * segmentBaseNode, SegmentInformation *info)
242 {
243     if(!segmentBaseNode)
244         return;
245
246     else if(segmentBaseNode->hasAttribute("indexRange"))
247     {
248         SegmentList *list = new SegmentList();
249         Segment *seg;
250
251         size_t start = 0, end = 0;
252         if (std::sscanf(segmentBaseNode->getAttributeValue("indexRange").c_str(), "%zu-%zu", &start, &end) == 2)
253         {
254             seg = new IndexSegment(info);
255             seg->setByteRange(start, end);
256             list->addSegment(seg);
257             /* index must be before data, so data starts at index end */
258             seg = new Segment(info);
259             seg->setByteRange(end + 1, 0);
260         }
261         else
262         {
263             seg = new Segment(info);
264         }
265
266         list->addSegment(seg);
267         info->setSegmentList(list);
268
269         Node *initSeg = DOMHelper::getFirstChildElementByName(segmentBaseNode, "Initialization");
270         if(initSeg)
271         {
272             SegmentBase *base = new SegmentBase();
273             parseInitSegment(initSeg, base);
274             info->setSegmentBase(base);
275         }
276     }
277     else
278     {
279         SegmentBase *base = new SegmentBase();
280         parseInitSegment(DOMHelper::getFirstChildElementByName(segmentBaseNode, "Initialization"), base);
281         info->setSegmentBase(base);
282     }
283 }
284
285 size_t IsoffMainParser::parseSegmentList(Node * segListNode, SegmentInformation *info)
286 {
287     size_t total = 0;
288     mtime_t totaltime = 0;
289     if(segListNode)
290     {
291         std::vector<Node *> segments = DOMHelper::getElementByTagName(segListNode, "SegmentURL", false);
292         SegmentList *list;
293         if(!segments.empty() && (list = new (std::nothrow) SegmentList()))
294         {
295             parseInitSegment(DOMHelper::getFirstChildElementByName(segListNode, "Initialization"), list);
296
297             if(segListNode->hasAttribute("duration"))
298                 list->setDuration(Integer<mtime_t>(segListNode->getAttributeValue("duration")));
299
300             if(segListNode->hasAttribute("timescale"))
301                 list->timescale.Set(Integer<uint64_t>(segListNode->getAttributeValue("timescale")));
302
303             std::vector<Node *>::const_iterator it;
304             for(it = segments.begin(); it != segments.end(); it++)
305             {
306                 Node *segmentURL = *it;
307                 std::string mediaUrl = segmentURL->getAttributeValue("media");
308                 if(mediaUrl.empty())
309                     continue;
310
311                 Segment *seg = new (std::nothrow) Segment(info);
312                 if(!seg)
313                     continue;
314
315                 seg->setSourceUrl(segmentURL->getAttributeValue("media"));
316
317                 if(segmentURL->hasAttribute("mediaRange"))
318                 {
319                     std::string range = segmentURL->getAttributeValue("mediaRange");
320                     size_t pos = range.find("-");
321                     seg->setByteRange(atoi(range.substr(0, pos).c_str()), atoi(range.substr(pos + 1, range.size()).c_str()));
322                 }
323
324                 if(totaltime || list->getDuration())
325                 {
326                     seg->startTime.Set(totaltime);
327                     totaltime += list->getDuration();
328                 }
329
330                 list->addSegment(seg);
331                 total++;
332             }
333
334             info->setSegmentList(list);
335         }
336     }
337     return total;
338 }
339
340 void IsoffMainParser::parseInitSegment(Node *initNode, Initializable<Segment> *init)
341 {
342     if(!initNode)
343         return;
344
345     Segment *seg = new InitSegment( currentRepresentation );
346     seg->setSourceUrl(initNode->getAttributeValue("sourceURL"));
347
348     if(initNode->hasAttribute("range"))
349     {
350         std::string range = initNode->getAttributeValue("range");
351         size_t pos = range.find("-");
352         seg->setByteRange(atoi(range.substr(0, pos).c_str()), atoi(range.substr(pos + 1, range.size()).c_str()));
353     }
354
355     init->initialisationSegment.Set(seg);
356 }
357
358 void IsoffMainParser::parseTimeline(Node *node, MediaSegmentTemplate *templ)
359 {
360     if(!node)
361         return;
362
363     SegmentTimeline *timeline = new (std::nothrow) SegmentTimeline(templ);
364     if(timeline)
365     {
366         std::vector<Node *> elements = DOMHelper::getElementByTagName(node, "S", false);
367         std::vector<Node *>::const_iterator it;
368         for(it = elements.begin(); it != elements.end(); it++)
369         {
370             const Node *s = *it;
371             if(!s->hasAttribute("d")) /* Mandatory */
372                 continue;
373             mtime_t d = Integer<mtime_t>(s->getAttributeValue("d"));
374             mtime_t r = 0; // never repeats by default
375             if(s->hasAttribute("r"))
376                 r = Integer<uint64_t>(s->getAttributeValue("r"));
377             if(s->hasAttribute("t"))
378             {
379                 mtime_t t = Integer<mtime_t>(s->getAttributeValue("t"));
380                 timeline->addElement(d, r, t);
381             }
382             else timeline->addElement(d, r);
383
384             templ->segmentTimeline.Set(timeline);
385         }
386     }
387 }
388
389 void IsoffMainParser::parseProgramInformation(Node * node, MPD *mpd)
390 {
391     if(!node)
392         return;
393
394     ProgramInformation *info = new (std::nothrow) ProgramInformation();
395     if (info)
396     {
397         Node *child = DOMHelper::getFirstChildElementByName(node, "Title");
398         if(child)
399             info->setTitle(child->getText());
400
401         child = DOMHelper::getFirstChildElementByName(node, "Source");
402         if(child)
403             info->setSource(child->getText());
404
405         child = DOMHelper::getFirstChildElementByName(node, "Copyright");
406         if(child)
407             info->setCopyright(child->getText());
408
409         if(node->hasAttribute("moreInformationURL"))
410             info->setMoreInformationUrl(node->getAttributeValue("moreInformationURL"));
411
412         mpd->programInfo.Set(info);
413     }
414 }
415
416 void    IsoffMainParser::print              ()
417 {
418     if(mpd)
419     {
420         msg_Dbg(p_stream, "MPD profile=%s mediaPresentationDuration=%ld minBufferTime=%ld",
421                 static_cast<std::string>(mpd->getProfile()).c_str(),
422                 mpd->duration.Get(),
423                 mpd->minBufferTime.Get());
424         msg_Dbg(p_stream, "BaseUrl=%s", mpd->getUrlSegment().toString().c_str());
425
426         std::vector<Period *>::const_iterator i;
427         for(i = mpd->getPeriods().begin(); i != mpd->getPeriods().end(); i++)
428         {
429             std::vector<std::string> debug = (*i)->toString();
430             std::vector<std::string>::const_iterator l;
431             for(l = debug.begin(); l < debug.end(); l++)
432             {
433                 msg_Dbg(p_stream, "%s", (*l).c_str());
434             }
435         }
436     }
437 }
438
439 IsoTime::IsoTime(const std::string &str)
440 {
441     time = str_duration(str.c_str());
442 }
443
444 IsoTime::operator mtime_t () const
445 {
446     return time;
447 }
448
449 /* i_year: year - 1900  i_month: 0-11  i_mday: 1-31 i_hour: 0-23 i_minute: 0-59 i_second: 0-59 */
450 static int64_t vlc_timegm( int i_year, int i_month, int i_mday, int i_hour, int i_minute, int i_second )
451 {
452     static const int pn_day[12+1] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
453     int64_t i_day;
454
455     if( i_year < 70 ||
456         i_month < 0 || i_month > 11 || i_mday < 1 || i_mday > 31 ||
457         i_hour < 0 || i_hour > 23 || i_minute < 0 || i_minute > 59 || i_second < 0 || i_second > 59 )
458         return -1;
459
460     /* Count the number of days */
461     i_day = (int64_t)365 * (i_year-70) + pn_day[i_month] + i_mday - 1;
462 #define LEAP(y) ( ((y)%4) == 0 && (((y)%100) != 0 || ((y)%400) == 0) ? 1 : 0)
463     for( int i = 70; i < i_year; i++ )
464         i_day += LEAP(1900+i);
465     if( i_month > 1 )
466         i_day += LEAP(1900+i_year);
467 #undef LEAP
468     /**/
469     return ((24*i_day + i_hour)*60 + i_minute)*60 + i_second;
470 }
471
472 UTCTime::UTCTime(const std::string &str)
473 {
474     enum { YEAR = 0, MON, DAY, HOUR, MIN, SEC, TZ };
475     int values[7] = {0};
476     std::istringstream in(str);
477
478     try
479     {
480         /* Date */
481         for(int i=YEAR;i<=DAY && !in.eof();i++)
482         {
483             if(i!=YEAR)
484                 in.ignore(1);
485             in >> values[i];
486         }
487         /* Time */
488         if (!in.eof() && in.peek() == 'T')
489         {
490             for(int i=HOUR;i<=SEC && !in.eof();i++)
491             {
492                 in.ignore(1);
493                 in >> values[i];
494             }
495         }
496         /* Timezone */
497         if (!in.eof() && in.peek() == 'Z')
498         {
499             in.ignore(1);
500             while(!in.eof())
501             {
502                 if(in.peek() == '+')
503                     continue;
504                 in >> values[TZ];
505                 break;
506             }
507         }
508
509         time = vlc_timegm( values[YEAR] - 1900, values[MON] - 1, values[DAY],
510                            values[HOUR], values[MIN], values[SEC] );
511         time += values[TZ] * 3600;
512     } catch(int) {
513         time = 0;
514     }
515 }
516
517 UTCTime::operator mtime_t () const
518 {
519     return time;
520 }