]> git.sesse.net Git - vlc/blob - modules/demux/dash/mpd/IsoffMainParser.cpp
msg: simplify msg_GenericVa() macro
[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("id"))
111             period->setId((*it)->getAttributeValue("id"));
112         setAdaptationSets(*it, period);
113         mpd->addPeriod(period);
114     }
115 }
116
117 size_t IsoffMainParser::parseSegmentTemplate(Node *templateNode, SegmentInformation *info)
118 {
119     size_t total = 0;
120     if (templateNode == NULL || !templateNode->hasAttribute("media"))
121         return total;
122
123     std::string mediaurl = templateNode->getAttributeValue("media");
124     MediaSegmentTemplate *mediaTemplate = NULL;
125     if(mediaurl.empty() || !(mediaTemplate = new (std::nothrow) MediaSegmentTemplate(info)) )
126         return total;
127     mediaTemplate->setSourceUrl(mediaurl);
128
129     if(templateNode->hasAttribute("startNumber"))
130         mediaTemplate->startNumber.Set(Integer<uint64_t>(templateNode->getAttributeValue("startNumber")));
131
132     if(templateNode->hasAttribute("duration"))
133         mediaTemplate->duration.Set(Integer<mtime_t>(templateNode->getAttributeValue("duration")));
134
135     if(templateNode->hasAttribute("timescale"))
136         mediaTemplate->timescale.Set(Integer<uint64_t>(templateNode->getAttributeValue("timescale")));
137
138     InitSegmentTemplate *initTemplate = NULL;
139
140     if(templateNode->hasAttribute("initialization"))
141     {
142         std::string initurl = templateNode->getAttributeValue("initialization");
143         if(!initurl.empty() && (initTemplate = new (std::nothrow) InitSegmentTemplate(info)))
144             initTemplate->setSourceUrl(initurl);
145     }
146
147     mediaTemplate->initialisationSegment.Set(initTemplate);
148     info->setSegmentTemplate(mediaTemplate);
149
150     parseTimeline(DOMHelper::getFirstChildElementByName(templateNode, "SegmentTimeline"), mediaTemplate);
151
152     total += ( mediaTemplate != NULL );
153
154     return total;
155 }
156
157 size_t IsoffMainParser::parseSegmentInformation(Node *node, SegmentInformation *info)
158 {
159     size_t total = 0;
160     parseSegmentBase(DOMHelper::getFirstChildElementByName(node, "SegmentBase"), info);
161     total += parseSegmentList(DOMHelper::getFirstChildElementByName(node, "SegmentList"), info);
162     total += parseSegmentTemplate(DOMHelper::getFirstChildElementByName(node, "SegmentTemplate" ), info);
163     if(node->hasAttribute("bitstreamSwitching"))
164         info->setBitstreamSwitching(node->getAttributeValue("bitstreamSwitching") == "true");
165     if(node->hasAttribute("timescale"))
166         info->timescale.Set(Integer<uint64_t>(node->getAttributeValue("timescale")));
167     return total;
168 }
169
170 void    IsoffMainParser::setAdaptationSets  (Node *periodNode, Period *period)
171 {
172     std::vector<Node *> adaptationSets = DOMHelper::getElementByTagName(periodNode, "AdaptationSet", false);
173     std::vector<Node *>::const_iterator it;
174
175     for(it = adaptationSets.begin(); it != adaptationSets.end(); it++)
176     {
177         AdaptationSet *adaptationSet = new AdaptationSet(period);
178         if(!adaptationSet)
179             continue;
180         if((*it)->hasAttribute("mimeType"))
181             adaptationSet->setMimeType((*it)->getAttributeValue("mimeType"));
182
183         parseSegmentInformation( *it, adaptationSet );
184
185         setRepresentations((*it), adaptationSet);
186         period->addAdaptationSet(adaptationSet);
187     }
188 }
189 void    IsoffMainParser::setRepresentations (Node *adaptationSetNode, AdaptationSet *adaptationSet)
190 {
191     std::vector<Node *> representations = DOMHelper::getElementByTagName(adaptationSetNode, "Representation", false);
192
193     for(size_t i = 0; i < representations.size(); i++)
194     {
195         this->currentRepresentation = new Representation(adaptationSet, getMPD());
196         Node *repNode = representations.at(i);
197
198         std::vector<Node *> baseUrls = DOMHelper::getChildElementByTagName(repNode, "BaseURL");
199         if(!baseUrls.empty())
200             currentRepresentation->setBaseUrl( new BaseUrl( baseUrls.front()->getText() ) );
201
202         if(repNode->hasAttribute("id"))
203             currentRepresentation->setId(repNode->getAttributeValue("id"));
204
205         if(repNode->hasAttribute("width"))
206             this->currentRepresentation->setWidth(atoi(repNode->getAttributeValue("width").c_str()));
207
208         if(repNode->hasAttribute("height"))
209             this->currentRepresentation->setHeight(atoi(repNode->getAttributeValue("height").c_str()));
210
211         if(repNode->hasAttribute("bandwidth"))
212             this->currentRepresentation->setBandwidth(atoi(repNode->getAttributeValue("bandwidth").c_str()));
213
214         if(repNode->hasAttribute("mimeType"))
215             currentRepresentation->setMimeType(repNode->getAttributeValue("mimeType"));
216
217         size_t totalmediasegments = parseSegmentInformation(repNode, currentRepresentation);
218         if(!totalmediasegments)
219         {
220             /* unranged & segment less representation, add fake segment */
221             SegmentList *list = new SegmentList();
222             Segment *seg = new Segment(currentRepresentation);
223             if(list && seg)
224             {
225                 list->addSegment(seg);
226                 currentRepresentation->setSegmentList(list);
227             }
228             else
229             {
230                 delete seg;
231                 delete list;
232             }
233         }
234
235         adaptationSet->addRepresentation(this->currentRepresentation);
236     }
237 }
238
239 void IsoffMainParser::parseSegmentBase(Node * segmentBaseNode, SegmentInformation *info)
240 {
241     if(!segmentBaseNode)
242         return;
243
244     else if(segmentBaseNode->hasAttribute("indexRange"))
245     {
246         SegmentList *list = new SegmentList();
247         Segment *seg;
248
249         size_t start = 0, end = 0;
250         if (std::sscanf(segmentBaseNode->getAttributeValue("indexRange").c_str(), "%zu-%zu", &start, &end) == 2)
251         {
252             seg = new IndexSegment(info);
253             seg->setByteRange(start, end);
254             list->addSegment(seg);
255             /* index must be before data, so data starts at index end */
256             seg = new Segment(info);
257             seg->setByteRange(end + 1, 0);
258         }
259         else
260         {
261             seg = new Segment(info);
262         }
263
264         list->addSegment(seg);
265         info->setSegmentList(list);
266
267         Node *initSeg = DOMHelper::getFirstChildElementByName(segmentBaseNode, "Initialization");
268         if(initSeg)
269         {
270             SegmentBase *base = new SegmentBase();
271             parseInitSegment(initSeg, base);
272             info->setSegmentBase(base);
273         }
274     }
275     else
276     {
277         SegmentBase *base = new SegmentBase();
278         parseInitSegment(DOMHelper::getFirstChildElementByName(segmentBaseNode, "Initialization"), base);
279         info->setSegmentBase(base);
280     }
281 }
282
283 size_t IsoffMainParser::parseSegmentList(Node * segListNode, SegmentInformation *info)
284 {
285     size_t total = 0;
286     mtime_t totaltime = 0;
287     if(segListNode)
288     {
289         std::vector<Node *> segments = DOMHelper::getElementByTagName(segListNode, "SegmentURL", false);
290         SegmentList *list;
291         if(!segments.empty() && (list = new (std::nothrow) SegmentList()))
292         {
293             parseInitSegment(DOMHelper::getFirstChildElementByName(segListNode, "Initialization"), list);
294
295             if(segListNode->hasAttribute("duration"))
296                 list->setDuration(Integer<mtime_t>(segListNode->getAttributeValue("duration")));
297
298             if(segListNode->hasAttribute("timescale"))
299                 list->timescale.Set(Integer<uint64_t>(segListNode->getAttributeValue("timescale")));
300
301             std::vector<Node *>::const_iterator it;
302             for(it = segments.begin(); it != segments.end(); it++)
303             {
304                 Node *segmentURL = *it;
305                 std::string mediaUrl = segmentURL->getAttributeValue("media");
306                 if(mediaUrl.empty())
307                     continue;
308
309                 Segment *seg = new (std::nothrow) Segment(info);
310                 if(!seg)
311                     continue;
312
313                 seg->setSourceUrl(segmentURL->getAttributeValue("media"));
314
315                 if(segmentURL->hasAttribute("mediaRange"))
316                 {
317                     std::string range = segmentURL->getAttributeValue("mediaRange");
318                     size_t pos = range.find("-");
319                     seg->setByteRange(atoi(range.substr(0, pos).c_str()), atoi(range.substr(pos + 1, range.size()).c_str()));
320                 }
321
322                 if(totaltime || list->getDuration())
323                 {
324                     seg->startTime.Set(totaltime);
325                     totaltime += list->getDuration();
326                 }
327
328                 list->addSegment(seg);
329                 total++;
330             }
331
332             info->setSegmentList(list);
333         }
334     }
335     return total;
336 }
337
338 void IsoffMainParser::parseInitSegment(Node *initNode, Initializable<Segment> *init)
339 {
340     if(!initNode)
341         return;
342
343     Segment *seg = new InitSegment( currentRepresentation );
344     seg->setSourceUrl(initNode->getAttributeValue("sourceURL"));
345
346     if(initNode->hasAttribute("range"))
347     {
348         std::string range = initNode->getAttributeValue("range");
349         size_t pos = range.find("-");
350         seg->setByteRange(atoi(range.substr(0, pos).c_str()), atoi(range.substr(pos + 1, range.size()).c_str()));
351     }
352
353     init->initialisationSegment.Set(seg);
354 }
355
356 void IsoffMainParser::parseTimeline(Node *node, MediaSegmentTemplate *templ)
357 {
358     if(!node)
359         return;
360
361     SegmentTimeline *timeline = new (std::nothrow) SegmentTimeline(templ);
362     if(timeline)
363     {
364         std::vector<Node *> elements = DOMHelper::getElementByTagName(node, "S", false);
365         std::vector<Node *>::const_iterator it;
366         for(it = elements.begin(); it != elements.end(); it++)
367         {
368             const Node *s = *it;
369             if(!s->hasAttribute("d")) /* Mandatory */
370                 continue;
371             mtime_t d = Integer<mtime_t>(s->getAttributeValue("d"));
372             mtime_t r = 0; // never repeats by default
373             if(s->hasAttribute("r"))
374                 r = Integer<uint64_t>(s->getAttributeValue("r"));
375             if(s->hasAttribute("t"))
376             {
377                 mtime_t t = Integer<mtime_t>(s->getAttributeValue("t"));
378                 timeline->addElement(d, r, t);
379             }
380             else timeline->addElement(d, r);
381
382             templ->segmentTimeline.Set(timeline);
383         }
384     }
385 }
386
387 void IsoffMainParser::parseProgramInformation(Node * node, MPD *mpd)
388 {
389     if(!node)
390         return;
391
392     ProgramInformation *info = new (std::nothrow) ProgramInformation();
393     if (info)
394     {
395         Node *child = DOMHelper::getFirstChildElementByName(node, "Title");
396         if(child)
397             info->setTitle(child->getText());
398
399         child = DOMHelper::getFirstChildElementByName(node, "Source");
400         if(child)
401             info->setSource(child->getText());
402
403         child = DOMHelper::getFirstChildElementByName(node, "Copyright");
404         if(child)
405             info->setCopyright(child->getText());
406
407         if(node->hasAttribute("moreInformationURL"))
408             info->setMoreInformationUrl(node->getAttributeValue("moreInformationURL"));
409
410         mpd->programInfo.Set(info);
411     }
412 }
413
414 void    IsoffMainParser::print              ()
415 {
416     if(mpd)
417     {
418         msg_Dbg(p_stream, "MPD profile=%s mediaPresentationDuration=%ld minBufferTime=%ld",
419                 static_cast<std::string>(mpd->getProfile()).c_str(),
420                 mpd->duration.Get(),
421                 mpd->minBufferTime.Get());
422         msg_Dbg(p_stream, "BaseUrl=%s", mpd->getUrlSegment().toString().c_str());
423
424         std::vector<Period *>::const_iterator i;
425         for(i = mpd->getPeriods().begin(); i != mpd->getPeriods().end(); i++)
426         {
427             std::vector<std::string> debug = (*i)->toString();
428             std::vector<std::string>::const_iterator l;
429             for(l = debug.begin(); l < debug.end(); l++)
430             {
431                 msg_Dbg(p_stream, "%s", (*l).c_str());
432             }
433         }
434     }
435 }
436
437 IsoTime::IsoTime(const std::string &str)
438 {
439     time = str_duration(str.c_str());
440 }
441
442 IsoTime::operator mtime_t () const
443 {
444     return time;
445 }
446
447 /* i_year: year - 1900  i_month: 0-11  i_mday: 1-31 i_hour: 0-23 i_minute: 0-59 i_second: 0-59 */
448 static int64_t vlc_timegm( int i_year, int i_month, int i_mday, int i_hour, int i_minute, int i_second )
449 {
450     static const int pn_day[12+1] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
451     int64_t i_day;
452
453     if( i_year < 70 ||
454         i_month < 0 || i_month > 11 || i_mday < 1 || i_mday > 31 ||
455         i_hour < 0 || i_hour > 23 || i_minute < 0 || i_minute > 59 || i_second < 0 || i_second > 59 )
456         return -1;
457
458     /* Count the number of days */
459     i_day = (int64_t)365 * (i_year-70) + pn_day[i_month] + i_mday - 1;
460 #define LEAP(y) ( ((y)%4) == 0 && (((y)%100) != 0 || ((y)%400) == 0) ? 1 : 0)
461     for( int i = 70; i < i_year; i++ )
462         i_day += LEAP(1900+i);
463     if( i_month > 1 )
464         i_day += LEAP(1900+i_year);
465 #undef LEAP
466     /**/
467     return ((24*i_day + i_hour)*60 + i_minute)*60 + i_second;
468 }
469
470 UTCTime::UTCTime(const std::string &str)
471 {
472     enum { YEAR = 0, MON, DAY, HOUR, MIN, SEC, TZ };
473     int values[7] = {0};
474     std::istringstream in(str);
475
476     try
477     {
478         /* Date */
479         for(int i=YEAR;i<=DAY && !in.eof();i++)
480         {
481             if(i!=YEAR)
482                 in.ignore(1);
483             in >> values[i];
484         }
485         /* Time */
486         if (!in.eof() && in.peek() == 'T')
487         {
488             for(int i=HOUR;i<=SEC && !in.eof();i++)
489             {
490                 in.ignore(1);
491                 in >> values[i];
492             }
493         }
494         /* Timezone */
495         if (!in.eof() && in.peek() == 'Z')
496         {
497             in.ignore(1);
498             while(!in.eof())
499             {
500                 if(in.peek() == '+')
501                     continue;
502                 in >> values[TZ];
503                 break;
504             }
505         }
506
507         time = vlc_timegm( values[YEAR] - 1900, values[MON] - 1, values[DAY],
508                            values[HOUR], values[MIN], values[SEC] );
509         time += values[TZ] * 3600;
510     } catch(int) {
511         time = 0;
512     }
513 }
514
515 UTCTime::operator mtime_t () const
516 {
517     return time;
518 }