3 *****************************************************************************
4 * Copyright (C) 2010 - 2012 Klagenfurt University
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>
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.
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.
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 *****************************************************************************/
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>
39 using namespace dash::mpd;
40 using namespace dash::xml;
42 IsoffMainParser::IsoffMainParser (Node *root, stream_t *p_stream) :
43 IMPDParser(root, NULL, p_stream, NULL)
46 IsoffMainParser::~IsoffMainParser ()
50 bool IsoffMainParser::parse (Profile profile)
52 mpd = new MPD(p_stream, profile);
54 parseProgramInformation(DOMHelper::getFirstChildElementByName(root, "ProgramInformation"), mpd);
62 void IsoffMainParser::setMPDAttributes ()
64 const std::map<std::string, std::string> attr = this->root->getAttributes();
66 std::map<std::string, std::string>::const_iterator it;
68 it = attr.find("mediaPresentationDuration");
70 this->mpd->duration.Set(IsoTime(it->second));
72 it = attr.find("minBufferTime");
74 this->mpd->minBufferTime.Set(IsoTime(it->second));
76 it = attr.find("minimumUpdatePeriod");
78 mpd->minUpdatePeriod.Set(IsoTime(it->second));
80 it = attr.find("maxSegmentDuration");
82 mpd->maxSegmentDuration.Set(IsoTime(it->second));
84 it = attr.find("type");
86 mpd->setType(it->second);
88 it = attr.find("availabilityStartTime");
90 mpd->availabilityStartTime.Set(UTCTime(it->second));
92 it = attr.find("timeShiftBufferDepth");
94 mpd->timeShiftBufferDepth.Set(IsoTime(it->second));
97 void IsoffMainParser::parsePeriods(Node *root)
99 std::vector<Node *> periods = DOMHelper::getElementByTagName(root, "Period", false);
100 std::vector<Node *>::const_iterator it;
102 for(it = periods.begin(); it != periods.end(); it++)
104 Period *period = new (std::nothrow) Period(mpd);
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);
117 size_t IsoffMainParser::parseSegmentTemplate(Node *templateNode, SegmentInformation *info)
120 if (templateNode == NULL || !templateNode->hasAttribute("media"))
123 std::string mediaurl = templateNode->getAttributeValue("media");
124 MediaSegmentTemplate *mediaTemplate = NULL;
125 if(mediaurl.empty() || !(mediaTemplate = new (std::nothrow) MediaSegmentTemplate(info)) )
127 mediaTemplate->setSourceUrl(mediaurl);
129 if(templateNode->hasAttribute("startNumber"))
130 mediaTemplate->startNumber.Set(Integer<uint64_t>(templateNode->getAttributeValue("startNumber")));
132 if(templateNode->hasAttribute("duration"))
133 mediaTemplate->duration.Set(Integer<mtime_t>(templateNode->getAttributeValue("duration")));
135 if(templateNode->hasAttribute("timescale"))
136 mediaTemplate->timescale.Set(Integer<uint64_t>(templateNode->getAttributeValue("timescale")));
138 InitSegmentTemplate *initTemplate = NULL;
140 if(templateNode->hasAttribute("initialization"))
142 std::string initurl = templateNode->getAttributeValue("initialization");
143 if(!initurl.empty() && (initTemplate = new (std::nothrow) InitSegmentTemplate(info)))
144 initTemplate->setSourceUrl(initurl);
147 mediaTemplate->initialisationSegment.Set(initTemplate);
148 info->setSegmentTemplate(mediaTemplate);
150 parseTimeline(DOMHelper::getFirstChildElementByName(templateNode, "SegmentTimeline"), mediaTemplate);
152 total += ( mediaTemplate != NULL );
157 size_t IsoffMainParser::parseSegmentInformation(Node *node, SegmentInformation *info)
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")));
170 void IsoffMainParser::setAdaptationSets (Node *periodNode, Period *period)
172 std::vector<Node *> adaptationSets = DOMHelper::getElementByTagName(periodNode, "AdaptationSet", false);
173 std::vector<Node *>::const_iterator it;
175 for(it = adaptationSets.begin(); it != adaptationSets.end(); it++)
177 AdaptationSet *adaptationSet = new AdaptationSet(period);
180 if((*it)->hasAttribute("mimeType"))
181 adaptationSet->setMimeType((*it)->getAttributeValue("mimeType"));
183 parseSegmentInformation( *it, adaptationSet );
185 setRepresentations((*it), adaptationSet);
186 period->addAdaptationSet(adaptationSet);
189 void IsoffMainParser::setRepresentations (Node *adaptationSetNode, AdaptationSet *adaptationSet)
191 std::vector<Node *> representations = DOMHelper::getElementByTagName(adaptationSetNode, "Representation", false);
193 for(size_t i = 0; i < representations.size(); i++)
195 this->currentRepresentation = new Representation(adaptationSet, getMPD());
196 Node *repNode = representations.at(i);
198 std::vector<Node *> baseUrls = DOMHelper::getChildElementByTagName(repNode, "BaseURL");
199 if(!baseUrls.empty())
200 currentRepresentation->setBaseUrl( new BaseUrl( baseUrls.front()->getText() ) );
202 if(repNode->hasAttribute("id"))
203 currentRepresentation->setId(repNode->getAttributeValue("id"));
205 if(repNode->hasAttribute("width"))
206 this->currentRepresentation->setWidth(atoi(repNode->getAttributeValue("width").c_str()));
208 if(repNode->hasAttribute("height"))
209 this->currentRepresentation->setHeight(atoi(repNode->getAttributeValue("height").c_str()));
211 if(repNode->hasAttribute("bandwidth"))
212 this->currentRepresentation->setBandwidth(atoi(repNode->getAttributeValue("bandwidth").c_str()));
214 if(repNode->hasAttribute("mimeType"))
215 currentRepresentation->setMimeType(repNode->getAttributeValue("mimeType"));
217 size_t totalmediasegments = parseSegmentInformation(repNode, currentRepresentation);
218 if(!totalmediasegments)
220 /* unranged & segment less representation, add fake segment */
221 SegmentList *list = new SegmentList();
222 Segment *seg = new Segment(currentRepresentation);
225 list->addSegment(seg);
226 currentRepresentation->setSegmentList(list);
235 adaptationSet->addRepresentation(this->currentRepresentation);
239 void IsoffMainParser::parseSegmentBase(Node * segmentBaseNode, SegmentInformation *info)
244 else if(segmentBaseNode->hasAttribute("indexRange"))
246 SegmentList *list = new SegmentList();
249 size_t start = 0, end = 0;
250 if (std::sscanf(segmentBaseNode->getAttributeValue("indexRange").c_str(), "%zu-%zu", &start, &end) == 2)
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);
261 seg = new Segment(info);
264 list->addSegment(seg);
265 info->setSegmentList(list);
267 Node *initSeg = DOMHelper::getFirstChildElementByName(segmentBaseNode, "Initialization");
270 SegmentBase *base = new SegmentBase();
271 parseInitSegment(initSeg, base);
272 info->setSegmentBase(base);
277 SegmentBase *base = new SegmentBase();
278 parseInitSegment(DOMHelper::getFirstChildElementByName(segmentBaseNode, "Initialization"), base);
279 info->setSegmentBase(base);
283 size_t IsoffMainParser::parseSegmentList(Node * segListNode, SegmentInformation *info)
286 mtime_t totaltime = 0;
289 std::vector<Node *> segments = DOMHelper::getElementByTagName(segListNode, "SegmentURL", false);
291 if(!segments.empty() && (list = new (std::nothrow) SegmentList()))
293 parseInitSegment(DOMHelper::getFirstChildElementByName(segListNode, "Initialization"), list);
295 if(segListNode->hasAttribute("duration"))
296 list->setDuration(Integer<mtime_t>(segListNode->getAttributeValue("duration")));
298 if(segListNode->hasAttribute("timescale"))
299 list->timescale.Set(Integer<uint64_t>(segListNode->getAttributeValue("timescale")));
301 std::vector<Node *>::const_iterator it;
302 for(it = segments.begin(); it != segments.end(); it++)
304 Node *segmentURL = *it;
305 std::string mediaUrl = segmentURL->getAttributeValue("media");
309 Segment *seg = new (std::nothrow) Segment(info);
313 seg->setSourceUrl(segmentURL->getAttributeValue("media"));
315 if(segmentURL->hasAttribute("mediaRange"))
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()));
322 if(totaltime || list->getDuration())
324 seg->startTime.Set(totaltime);
325 totaltime += list->getDuration();
328 list->addSegment(seg);
332 info->setSegmentList(list);
338 void IsoffMainParser::parseInitSegment(Node *initNode, Initializable<Segment> *init)
343 Segment *seg = new InitSegment( currentRepresentation );
344 seg->setSourceUrl(initNode->getAttributeValue("sourceURL"));
346 if(initNode->hasAttribute("range"))
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()));
353 init->initialisationSegment.Set(seg);
356 void IsoffMainParser::parseTimeline(Node *node, MediaSegmentTemplate *templ)
361 SegmentTimeline *timeline = new (std::nothrow) SegmentTimeline(templ);
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++)
369 if(!s->hasAttribute("d")) /* Mandatory */
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"))
377 mtime_t t = Integer<mtime_t>(s->getAttributeValue("t"));
378 timeline->addElement(d, r, t);
380 else timeline->addElement(d, r);
382 templ->segmentTimeline.Set(timeline);
387 void IsoffMainParser::parseProgramInformation(Node * node, MPD *mpd)
392 ProgramInformation *info = new (std::nothrow) ProgramInformation();
395 Node *child = DOMHelper::getFirstChildElementByName(node, "Title");
397 info->setTitle(child->getText());
399 child = DOMHelper::getFirstChildElementByName(node, "Source");
401 info->setSource(child->getText());
403 child = DOMHelper::getFirstChildElementByName(node, "Copyright");
405 info->setCopyright(child->getText());
407 if(node->hasAttribute("moreInformationURL"))
408 info->setMoreInformationUrl(node->getAttributeValue("moreInformationURL"));
410 mpd->programInfo.Set(info);
414 void IsoffMainParser::print ()
418 msg_Dbg(p_stream, "MPD profile=%s mediaPresentationDuration=%ld minBufferTime=%ld",
419 static_cast<std::string>(mpd->getProfile()).c_str(),
421 mpd->minBufferTime.Get());
422 msg_Dbg(p_stream, "BaseUrl=%s", mpd->getUrlSegment().toString().c_str());
424 std::vector<Period *>::const_iterator i;
425 for(i = mpd->getPeriods().begin(); i != mpd->getPeriods().end(); i++)
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++)
431 msg_Dbg(p_stream, "%s", (*l).c_str());
437 IsoTime::IsoTime(const std::string &str)
439 time = str_duration(str.c_str());
442 IsoTime::operator mtime_t () const
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 )
450 static const int pn_day[12+1] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
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 )
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);
464 i_day += LEAP(1900+i_year);
467 return ((24*i_day + i_hour)*60 + i_minute)*60 + i_second;
470 UTCTime::UTCTime(const std::string &str)
472 enum { YEAR = 0, MON, DAY, HOUR, MIN, SEC, TZ };
474 std::istringstream in(str);
479 for(int i=YEAR;i<=DAY && !in.eof();i++)
486 if (!in.eof() && in.peek() == 'T')
488 for(int i=HOUR;i<=SEC && !in.eof();i++)
495 if (!in.eof() && in.peek() == 'Z')
507 time = vlc_timegm( values[YEAR] - 1900, values[MON] - 1, values[DAY],
508 values[HOUR], values[MIN], values[SEC] );
509 time += values[TZ] * 3600;
515 UTCTime::operator mtime_t () const