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("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);
119 size_t IsoffMainParser::parseSegmentTemplate(Node *templateNode, SegmentInformation *info)
122 if (templateNode == NULL || !templateNode->hasAttribute("media"))
125 std::string mediaurl = templateNode->getAttributeValue("media");
126 MediaSegmentTemplate *mediaTemplate = NULL;
127 if(mediaurl.empty() || !(mediaTemplate = new (std::nothrow) MediaSegmentTemplate(info)) )
129 mediaTemplate->setSourceUrl(mediaurl);
131 if(templateNode->hasAttribute("startNumber"))
132 mediaTemplate->startNumber.Set(Integer<uint64_t>(templateNode->getAttributeValue("startNumber")));
134 if(templateNode->hasAttribute("duration"))
135 mediaTemplate->duration.Set(Integer<mtime_t>(templateNode->getAttributeValue("duration")));
137 if(templateNode->hasAttribute("timescale"))
138 mediaTemplate->timescale.Set(Integer<uint64_t>(templateNode->getAttributeValue("timescale")));
140 InitSegmentTemplate *initTemplate = NULL;
142 if(templateNode->hasAttribute("initialization"))
144 std::string initurl = templateNode->getAttributeValue("initialization");
145 if(!initurl.empty() && (initTemplate = new (std::nothrow) InitSegmentTemplate(info)))
146 initTemplate->setSourceUrl(initurl);
149 mediaTemplate->initialisationSegment.Set(initTemplate);
150 info->setSegmentTemplate(mediaTemplate);
152 parseTimeline(DOMHelper::getFirstChildElementByName(templateNode, "SegmentTimeline"), mediaTemplate);
154 total += ( mediaTemplate != NULL );
159 size_t IsoffMainParser::parseSegmentInformation(Node *node, SegmentInformation *info)
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")));
172 void IsoffMainParser::setAdaptationSets (Node *periodNode, Period *period)
174 std::vector<Node *> adaptationSets = DOMHelper::getElementByTagName(periodNode, "AdaptationSet", false);
175 std::vector<Node *>::const_iterator it;
177 for(it = adaptationSets.begin(); it != adaptationSets.end(); it++)
179 AdaptationSet *adaptationSet = new AdaptationSet(period);
182 if((*it)->hasAttribute("mimeType"))
183 adaptationSet->setMimeType((*it)->getAttributeValue("mimeType"));
185 parseSegmentInformation( *it, adaptationSet );
187 setRepresentations((*it), adaptationSet);
188 period->addAdaptationSet(adaptationSet);
191 void IsoffMainParser::setRepresentations (Node *adaptationSetNode, AdaptationSet *adaptationSet)
193 std::vector<Node *> representations = DOMHelper::getElementByTagName(adaptationSetNode, "Representation", false);
195 for(size_t i = 0; i < representations.size(); i++)
197 this->currentRepresentation = new Representation(adaptationSet, getMPD());
198 Node *repNode = representations.at(i);
200 std::vector<Node *> baseUrls = DOMHelper::getChildElementByTagName(repNode, "BaseURL");
201 if(!baseUrls.empty())
202 currentRepresentation->setBaseUrl( new BaseUrl( baseUrls.front()->getText() ) );
204 if(repNode->hasAttribute("id"))
205 currentRepresentation->setId(repNode->getAttributeValue("id"));
207 if(repNode->hasAttribute("width"))
208 this->currentRepresentation->setWidth(atoi(repNode->getAttributeValue("width").c_str()));
210 if(repNode->hasAttribute("height"))
211 this->currentRepresentation->setHeight(atoi(repNode->getAttributeValue("height").c_str()));
213 if(repNode->hasAttribute("bandwidth"))
214 this->currentRepresentation->setBandwidth(atoi(repNode->getAttributeValue("bandwidth").c_str()));
216 if(repNode->hasAttribute("mimeType"))
217 currentRepresentation->setMimeType(repNode->getAttributeValue("mimeType"));
219 size_t totalmediasegments = parseSegmentInformation(repNode, currentRepresentation);
220 if(!totalmediasegments)
222 /* unranged & segment less representation, add fake segment */
223 SegmentList *list = new SegmentList();
224 Segment *seg = new Segment(currentRepresentation);
227 list->addSegment(seg);
228 currentRepresentation->setSegmentList(list);
237 adaptationSet->addRepresentation(this->currentRepresentation);
241 void IsoffMainParser::parseSegmentBase(Node * segmentBaseNode, SegmentInformation *info)
246 else if(segmentBaseNode->hasAttribute("indexRange"))
248 SegmentList *list = new SegmentList();
251 size_t start = 0, end = 0;
252 if (std::sscanf(segmentBaseNode->getAttributeValue("indexRange").c_str(), "%zu-%zu", &start, &end) == 2)
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);
263 seg = new Segment(info);
266 list->addSegment(seg);
267 info->setSegmentList(list);
269 Node *initSeg = DOMHelper::getFirstChildElementByName(segmentBaseNode, "Initialization");
272 SegmentBase *base = new SegmentBase();
273 parseInitSegment(initSeg, base);
274 info->setSegmentBase(base);
279 SegmentBase *base = new SegmentBase();
280 parseInitSegment(DOMHelper::getFirstChildElementByName(segmentBaseNode, "Initialization"), base);
281 info->setSegmentBase(base);
285 size_t IsoffMainParser::parseSegmentList(Node * segListNode, SegmentInformation *info)
288 mtime_t totaltime = 0;
291 std::vector<Node *> segments = DOMHelper::getElementByTagName(segListNode, "SegmentURL", false);
293 if(!segments.empty() && (list = new (std::nothrow) SegmentList()))
295 parseInitSegment(DOMHelper::getFirstChildElementByName(segListNode, "Initialization"), list);
297 if(segListNode->hasAttribute("duration"))
298 list->setDuration(Integer<mtime_t>(segListNode->getAttributeValue("duration")));
300 if(segListNode->hasAttribute("timescale"))
301 list->timescale.Set(Integer<uint64_t>(segListNode->getAttributeValue("timescale")));
303 std::vector<Node *>::const_iterator it;
304 for(it = segments.begin(); it != segments.end(); it++)
306 Node *segmentURL = *it;
307 std::string mediaUrl = segmentURL->getAttributeValue("media");
311 Segment *seg = new (std::nothrow) Segment(info);
315 seg->setSourceUrl(segmentURL->getAttributeValue("media"));
317 if(segmentURL->hasAttribute("mediaRange"))
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()));
324 if(totaltime || list->getDuration())
326 seg->startTime.Set(totaltime);
327 totaltime += list->getDuration();
330 list->addSegment(seg);
334 info->setSegmentList(list);
340 void IsoffMainParser::parseInitSegment(Node *initNode, Initializable<Segment> *init)
345 Segment *seg = new InitSegment( currentRepresentation );
346 seg->setSourceUrl(initNode->getAttributeValue("sourceURL"));
348 if(initNode->hasAttribute("range"))
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()));
355 init->initialisationSegment.Set(seg);
358 void IsoffMainParser::parseTimeline(Node *node, MediaSegmentTemplate *templ)
363 SegmentTimeline *timeline = new (std::nothrow) SegmentTimeline(templ);
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++)
371 if(!s->hasAttribute("d")) /* Mandatory */
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"))
379 mtime_t t = Integer<mtime_t>(s->getAttributeValue("t"));
380 timeline->addElement(d, r, t);
382 else timeline->addElement(d, r);
384 templ->segmentTimeline.Set(timeline);
389 void IsoffMainParser::parseProgramInformation(Node * node, MPD *mpd)
394 ProgramInformation *info = new (std::nothrow) ProgramInformation();
397 Node *child = DOMHelper::getFirstChildElementByName(node, "Title");
399 info->setTitle(child->getText());
401 child = DOMHelper::getFirstChildElementByName(node, "Source");
403 info->setSource(child->getText());
405 child = DOMHelper::getFirstChildElementByName(node, "Copyright");
407 info->setCopyright(child->getText());
409 if(node->hasAttribute("moreInformationURL"))
410 info->setMoreInformationUrl(node->getAttributeValue("moreInformationURL"));
412 mpd->programInfo.Set(info);
416 void IsoffMainParser::print ()
420 msg_Dbg(p_stream, "MPD profile=%s mediaPresentationDuration=%ld minBufferTime=%ld",
421 static_cast<std::string>(mpd->getProfile()).c_str(),
423 mpd->minBufferTime.Get());
424 msg_Dbg(p_stream, "BaseUrl=%s", mpd->getUrlSegment().toString().c_str());
426 std::vector<Period *>::const_iterator i;
427 for(i = mpd->getPeriods().begin(); i != mpd->getPeriods().end(); i++)
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++)
433 msg_Dbg(p_stream, "%s", (*l).c_str());
439 IsoTime::IsoTime(const std::string &str)
441 time = str_duration(str.c_str());
444 IsoTime::operator mtime_t () const
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 )
452 static const int pn_day[12+1] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
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 )
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);
466 i_day += LEAP(1900+i_year);
469 return ((24*i_day + i_hour)*60 + i_minute)*60 + i_second;
472 UTCTime::UTCTime(const std::string &str)
474 enum { YEAR = 0, MON, DAY, HOUR, MIN, SEC, TZ };
476 std::istringstream in(str);
481 for(int i=YEAR;i<=DAY && !in.eof();i++)
488 if (!in.eof() && in.peek() == 'T')
490 for(int i=HOUR;i<=SEC && !in.eof();i++)
497 if (!in.eof() && in.peek() == 'Z')
509 time = vlc_timegm( values[YEAR] - 1900, values[MON] - 1, values[DAY],
510 values[HOUR], values[MIN], values[SEC] );
511 time += values[TZ] * 3600;
517 UTCTime::operator mtime_t () const