]> git.sesse.net Git - vlc/blob - modules/demux/dash/mpd/IsoffMainParser.cpp
9271e415f48f0c9bc98c9548263878a5511d0fe4
[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 "ProgramInformation.h"
32 #include "xml/DOMHelper.h"
33 #include <vlc_strings.h>
34 #include <vlc_stream.h>
35 #include <cstdio>
36 #include <sstream>
37
38 using namespace dash::mpd;
39 using namespace dash::xml;
40
41 IsoffMainParser::IsoffMainParser    (Node *root, stream_t *p_stream) :
42                  IMPDParser(root, NULL, p_stream, NULL)
43 {
44 }
45 IsoffMainParser::~IsoffMainParser   ()
46 {
47 }
48
49 bool    IsoffMainParser::parse              (Profile profile)
50 {
51     mpd = new MPD(p_stream, profile);
52     setMPDAttributes();
53     parseProgramInformation(DOMHelper::getFirstChildElementByName(root, "ProgramInformation"), mpd);
54     setMPDBaseUrl(root);
55     parsePeriods(root);
56
57     print();
58     return true;
59 }
60
61 void    IsoffMainParser::setMPDAttributes   ()
62 {
63     const std::map<std::string, std::string> attr = this->root->getAttributes();
64
65     std::map<std::string, std::string>::const_iterator it;
66
67     it = attr.find("mediaPresentationDuration");
68     if(it != attr.end())
69         this->mpd->setDuration(IsoTime(it->second));
70
71     it = attr.find("minBufferTime");
72     if(it != attr.end())
73         this->mpd->setMinBufferTime(IsoTime(it->second));
74
75     it = attr.find("type");
76     if(it != attr.end())
77         mpd->setType(it->second);
78
79     it = attr.find("availabilityStartTime");
80     if(it != attr.end())
81         mpd->setAvailabilityStartTime(UTCTime(it->second));
82
83     it = attr.find("timeShiftBufferDepth");
84         if(it != attr.end())
85             mpd->setTimeShiftBufferDepth(IsoTime(it->second));
86 }
87
88 void IsoffMainParser::parsePeriods(Node *root)
89 {
90     std::vector<Node *> periods = DOMHelper::getElementByTagName(root, "Period", false);
91     std::vector<Node *>::const_iterator it;
92
93     for(it = periods.begin(); it != periods.end(); it++)
94     {
95         Period *period = new (std::nothrow) Period(mpd);
96         if (!period)
97             continue;
98         parseSegmentInformation(*it, period);
99         if((*it)->hasAttribute("start"))
100             period->startTime.Set(IsoTime((*it)->getAttributeValue("start")));
101         setAdaptationSets(*it, period);
102         mpd->addPeriod(period);
103     }
104 }
105
106 size_t IsoffMainParser::parseSegmentTemplate(Node *templateNode, SegmentInformation *info)
107 {
108     size_t total = 0;
109     if (templateNode == NULL || !templateNode->hasAttribute("media"))
110         return total;
111
112     std::string mediaurl = templateNode->getAttributeValue("media");
113     SegmentTemplate *mediaTemplate = NULL;
114     if(mediaurl.empty() || !(mediaTemplate = new (std::nothrow) SegmentTemplate(info)) )
115         return total;
116     mediaTemplate->setSourceUrl(mediaurl);
117
118     if(templateNode->hasAttribute("startNumber"))
119         mediaTemplate->setStartIndex(Integer<uint64_t>(templateNode->getAttributeValue("startNumber")));
120
121     if(templateNode->hasAttribute("duration"))
122         mediaTemplate->duration.Set(Integer<mtime_t>(templateNode->getAttributeValue("duration")));
123
124     if(templateNode->hasAttribute("timescale"))
125         mediaTemplate->timescale.Set(Integer<uint64_t>(templateNode->getAttributeValue("timescale")));
126
127     InitSegmentTemplate *initTemplate = NULL;
128
129     if(templateNode->hasAttribute("initialization"))
130     {
131         std::string initurl = templateNode->getAttributeValue("initialization");
132         if(!initurl.empty() && (initTemplate = new (std::nothrow) InitSegmentTemplate(info)))
133             initTemplate->setSourceUrl(initurl);
134     }
135
136     info->setSegmentTemplate(mediaTemplate, SegmentInformation::INFOTYPE_MEDIA);
137     info->setSegmentTemplate(initTemplate, SegmentInformation::INFOTYPE_INIT);
138
139     total += ( mediaTemplate != NULL );
140
141     return total;
142 }
143
144 size_t IsoffMainParser::parseSegmentInformation(Node *node, SegmentInformation *info)
145 {
146     size_t total = 0;
147     parseSegmentBase(DOMHelper::getFirstChildElementByName(node, "SegmentBase"), info);
148     total += parseSegmentList(DOMHelper::getFirstChildElementByName(node, "SegmentList"), info);
149     total += parseSegmentTemplate(DOMHelper::getFirstChildElementByName(node, "SegmentTemplate" ), info);
150     if(node->hasAttribute("bitstreamSwitching"))
151         info->setBitstreamSwitching(node->getAttributeValue("bitstreamSwitching") == "true");
152     if(node->hasAttribute("timescale"))
153         info->timescale.Set(Integer<uint64_t>(node->getAttributeValue("timescale")));
154     return total;
155 }
156
157 void    IsoffMainParser::setAdaptationSets  (Node *periodNode, Period *period)
158 {
159     std::vector<Node *> adaptationSets = DOMHelper::getElementByTagName(periodNode, "AdaptationSet", false);
160     std::vector<Node *>::const_iterator it;
161
162     for(it = adaptationSets.begin(); it != adaptationSets.end(); it++)
163     {
164         AdaptationSet *adaptationSet = new AdaptationSet(period);
165         if(!adaptationSet)
166             continue;
167         if((*it)->hasAttribute("mimeType"))
168             adaptationSet->setMimeType((*it)->getAttributeValue("mimeType"));
169
170         parseSegmentInformation( *it, adaptationSet );
171
172         setRepresentations((*it), adaptationSet);
173         period->addAdaptationSet(adaptationSet);
174     }
175 }
176 void    IsoffMainParser::setRepresentations (Node *adaptationSetNode, AdaptationSet *adaptationSet)
177 {
178     std::vector<Node *> representations = DOMHelper::getElementByTagName(adaptationSetNode, "Representation", false);
179
180     for(size_t i = 0; i < representations.size(); i++)
181     {
182         this->currentRepresentation = new Representation(adaptationSet, getMPD());
183         Node *repNode = representations.at(i);
184
185         std::vector<Node *> baseUrls = DOMHelper::getChildElementByTagName(repNode, "BaseURL");
186         if(!baseUrls.empty())
187             currentRepresentation->setBaseUrl( new BaseUrl( baseUrls.front()->getText() ) );
188
189         if(repNode->hasAttribute("id"))
190             currentRepresentation->setId(repNode->getAttributeValue("id"));
191
192         if(repNode->hasAttribute("width"))
193             this->currentRepresentation->setWidth(atoi(repNode->getAttributeValue("width").c_str()));
194
195         if(repNode->hasAttribute("height"))
196             this->currentRepresentation->setHeight(atoi(repNode->getAttributeValue("height").c_str()));
197
198         if(repNode->hasAttribute("bandwidth"))
199             this->currentRepresentation->setBandwidth(atoi(repNode->getAttributeValue("bandwidth").c_str()));
200
201         if(repNode->hasAttribute("mimeType"))
202             currentRepresentation->setMimeType(repNode->getAttributeValue("mimeType"));
203
204         size_t totalmediasegments = parseSegmentInformation(repNode, currentRepresentation);
205         if(!totalmediasegments)
206         {
207             /* unranged & segment less representation, add fake segment */
208             SegmentList *list = new SegmentList();
209             Segment *seg = new Segment(currentRepresentation);
210             if(list && seg)
211             {
212                 list->addSegment(seg);
213                 currentRepresentation->setSegmentList(list);
214             }
215             else
216             {
217                 delete seg;
218                 delete list;
219             }
220         }
221
222         adaptationSet->addRepresentation(this->currentRepresentation);
223     }
224 }
225
226 void IsoffMainParser::parseSegmentBase(Node * segmentBaseNode, SegmentInformation *info)
227 {
228     if(!segmentBaseNode)
229         return;
230
231     else if(segmentBaseNode->hasAttribute("indexRange"))
232     {
233         SegmentList *list = new SegmentList();
234         Segment *seg;
235
236         size_t start = 0, end = 0;
237         if (std::sscanf(segmentBaseNode->getAttributeValue("indexRange").c_str(), "%zu-%zu", &start, &end) == 2)
238         {
239             seg = new IndexSegment(info);
240             seg->setByteRange(start, end);
241             list->addSegment(seg);
242             /* index must be before data, so data starts at index end */
243             seg = new Segment(info);
244             seg->setByteRange(end + 1, 0);
245         }
246         else
247         {
248             seg = new Segment(info);
249         }
250
251         list->addSegment(seg);
252         info->setSegmentList(list);
253
254         std::vector<Node *> initSeg = DOMHelper::getElementByTagName(segmentBaseNode, "Initialization", false);
255         if(!initSeg.empty())
256         {
257             SegmentBase *base = new SegmentBase();
258             setInitSegment(segmentBaseNode, base);
259             info->setSegmentBase(base);
260         }
261     }
262     else
263     {
264         SegmentBase *base = new SegmentBase();
265         setInitSegment(segmentBaseNode, base);
266         info->setSegmentBase(base);
267     }
268 }
269
270 size_t IsoffMainParser::parseSegmentList(Node * segListNode, SegmentInformation *info)
271 {
272     size_t total = 0;
273     mtime_t totaltime = 0;
274     if(segListNode)
275     {
276         std::vector<Node *> segments = DOMHelper::getElementByTagName(segListNode, "SegmentURL", false);
277         SegmentList *list;
278         if(!segments.empty() && (list = new (std::nothrow) SegmentList()))
279         {
280             if(segListNode->hasAttribute("duration"))
281                 list->setDuration(Integer<mtime_t>(segListNode->getAttributeValue("duration")));
282
283             std::vector<Node *>::const_iterator it;
284             for(it = segments.begin(); it != segments.end(); it++)
285             {
286                 Node *segmentURL = *it;
287                 std::string mediaUrl = segmentURL->getAttributeValue("media");
288                 if(mediaUrl.empty())
289                     continue;
290
291                 Segment *seg = new (std::nothrow) Segment(info);
292                 if(!seg)
293                     continue;
294
295                 seg->setSourceUrl(segmentURL->getAttributeValue("media"));
296
297                 if(segmentURL->hasAttribute("mediaRange"))
298                 {
299                     std::string range = segmentURL->getAttributeValue("mediaRange");
300                     size_t pos = range.find("-");
301                     seg->setByteRange(atoi(range.substr(0, pos).c_str()), atoi(range.substr(pos + 1, range.size()).c_str()));
302                 }
303
304                 if(totaltime || list->getDuration())
305                 {
306                     seg->startTime.Set(totaltime);
307                     totaltime += list->getDuration();
308                 }
309
310                 list->addSegment(seg);
311                 total++;
312             }
313
314             info->setSegmentList(list);
315         }
316     }
317     return total;
318 }
319
320 void    IsoffMainParser::setInitSegment     (dash::xml::Node *segBaseNode, SegmentBase *base)
321 {
322     std::vector<Node *> initSeg = DOMHelper::getElementByTagName(segBaseNode, "Initialisation", false);
323
324     if(initSeg.size() == 0)
325         initSeg = DOMHelper::getElementByTagName(segBaseNode, "Initialization", false);
326
327     if(initSeg.size() > 0)
328     {
329         Segment *seg = new InitSegment( currentRepresentation );
330         seg->setSourceUrl(initSeg.at(0)->getAttributeValue("sourceURL"));
331
332         if(initSeg.at(0)->hasAttribute("range"))
333         {
334             std::string range = initSeg.at(0)->getAttributeValue("range");
335             size_t pos = range.find("-");
336             seg->setByteRange(atoi(range.substr(0, pos).c_str()), atoi(range.substr(pos + 1, range.size()).c_str()));
337         }
338
339         base->addInitSegment(seg);
340     }
341 }
342
343 void IsoffMainParser::parseProgramInformation(Node * node, MPD *mpd)
344 {
345     if(!node)
346         return;
347
348     ProgramInformation *info = new (std::nothrow) ProgramInformation();
349     if (info)
350     {
351         Node *child = DOMHelper::getFirstChildElementByName(node, "Title");
352         if(child)
353             info->setTitle(child->getText());
354
355         child = DOMHelper::getFirstChildElementByName(node, "Source");
356         if(child)
357             info->setSource(child->getText());
358
359         child = DOMHelper::getFirstChildElementByName(node, "Copyright");
360         if(child)
361             info->setCopyright(child->getText());
362
363         if(node->hasAttribute("moreInformationURL"))
364             info->setMoreInformationUrl(node->getAttributeValue("moreInformationURL"));
365
366         mpd->setProgramInformation(info);
367     }
368 }
369
370 void    IsoffMainParser::print              ()
371 {
372     if(mpd)
373     {
374         msg_Dbg(p_stream, "MPD profile=%s mediaPresentationDuration=%ld minBufferTime=%ld",
375                 static_cast<std::string>(mpd->getProfile()).c_str(),
376                 mpd->getDuration(),
377                 mpd->getMinBufferTime());
378         msg_Dbg(p_stream, "BaseUrl=%s", mpd->getUrlSegment().toString().c_str());
379
380         std::vector<Period *>::const_iterator i;
381         for(i = mpd->getPeriods().begin(); i != mpd->getPeriods().end(); i++)
382         {
383             std::vector<std::string> debug = (*i)->toString();
384             std::vector<std::string>::const_iterator l;
385             for(l = debug.begin(); l < debug.end(); l++)
386             {
387                 msg_Dbg(p_stream, "%s", (*l).c_str());
388             }
389         }
390     }
391 }
392
393 IsoTime::IsoTime(const std::string &str)
394 {
395     time = str_duration(str.c_str());
396 }
397
398 IsoTime::operator mtime_t () const
399 {
400     return time;
401 }
402
403 /* i_year: year - 1900  i_month: 0-11  i_mday: 1-31 i_hour: 0-23 i_minute: 0-59 i_second: 0-59 */
404 static int64_t vlc_timegm( int i_year, int i_month, int i_mday, int i_hour, int i_minute, int i_second )
405 {
406     static const int pn_day[12+1] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
407     int64_t i_day;
408
409     if( i_year < 70 ||
410         i_month < 0 || i_month > 11 || i_mday < 1 || i_mday > 31 ||
411         i_hour < 0 || i_hour > 23 || i_minute < 0 || i_minute > 59 || i_second < 0 || i_second > 59 )
412         return -1;
413
414     /* Count the number of days */
415     i_day = 365 * (i_year-70) + pn_day[i_month] + i_mday - 1;
416 #define LEAP(y) ( ((y)%4) == 0 && (((y)%100) != 0 || ((y)%400) == 0) ? 1 : 0)
417     for( int i = 70; i < i_year; i++ )
418         i_day += LEAP(1900+i);
419     if( i_month > 1 )
420         i_day += LEAP(1900+i_year);
421 #undef LEAP
422     /**/
423     return ((24*i_day + i_hour)*60 + i_minute)*60 + i_second;
424 }
425
426 UTCTime::UTCTime(const std::string &str)
427 {
428     enum { YEAR = 0, MON, DAY, HOUR, MIN, SEC, TZ };
429     int values[7] = {0};
430     std::istringstream in(str);
431
432     try
433     {
434         /* Date */
435         for(int i=YEAR;i<=DAY && !in.eof();i++)
436         {
437             if(i!=YEAR)
438                 in.ignore(1);
439             in >> values[i];
440         }
441         /* Time */
442         if (!in.eof() && in.peek() == 'T')
443         {
444             for(int i=HOUR;i<=SEC && !in.eof();i++)
445             {
446                 in.ignore(1);
447                 in >> values[i];
448             }
449         }
450         /* Timezone */
451         if (!in.eof() && in.peek() == 'Z')
452         {
453             in.ignore(1);
454             while(!in.eof())
455             {
456                 if(in.peek() == '+')
457                     continue;
458                 in >> values[TZ];
459                 break;
460             }
461         }
462
463         time = vlc_timegm( values[YEAR] - 1900, values[MON] - 1, values[DAY],
464                            values[HOUR], values[MIN], values[SEC] );
465         time += values[TZ] * 3600;
466     } catch(int) {
467         time = 0;
468     }
469 }
470
471 UTCTime::operator mtime_t () const
472 {
473     return time;
474 }