#endif
#include "IsoffMainParser.h"
+#include "SegmentTemplate.h"
+#include "SegmentInfoDefault.h"
#include "xml/DOMHelper.h"
#include <vlc_strings.h>
#include <vlc_stream.h>
#include <cstdio>
+#include <sstream>
using namespace dash::mpd;
using namespace dash::xml;
mpd = new MPD(p_stream, profile);
setMPDAttributes();
setMPDBaseUrl(root);
- setPeriods(root);
+ parsePeriods(root);
+
print();
return true;
}
it = attr.find("mediaPresentationDuration");
if(it != attr.end())
- this->mpd->setDuration(str_duration(it->second.c_str()));
+ this->mpd->setDuration(IsoTime(it->second));
it = attr.find("minBufferTime");
if(it != attr.end())
- this->mpd->setMinBufferTime(str_duration( it->second.c_str()));
+ this->mpd->setMinBufferTime(IsoTime(it->second));
+
+ it = attr.find("type");
+ if(it != attr.end())
+ mpd->setType(it->second);
+
+ it = attr.find("availabilityStartTime");
+ if(it != attr.end())
+ mpd->setAvailabilityStartTime(UTCTime(it->second));
+
+ it = attr.find("timeShiftBufferDepth");
+ if(it != attr.end())
+ mpd->setTimeShiftBufferDepth(IsoTime(it->second));
+}
+void IsoffMainParser::parsePeriods(Node *root)
+{
+ std::vector<Node *> periods = DOMHelper::getElementByTagName(root, "Period", false);
+ std::vector<Node *>::const_iterator it;
+
+ for(it = periods.begin(); it != periods.end(); it++)
+ {
+ Period *period = new (std::nothrow) Period(mpd);
+ if (!period)
+ continue;
+ parseSegmentInformation(*it, period);
+ if((*it)->hasAttribute("start"))
+ period->startTime.Set(IsoTime((*it)->getAttributeValue("start")));
+ setAdaptationSets(*it, period);
+ mpd->addPeriod(period);
+ }
+}
+
+size_t IsoffMainParser::parseSegmentTemplate(Node *templateNode, SegmentInformation *info)
+{
+ size_t total = 0;
+ if (templateNode == NULL || !templateNode->hasAttribute("media"))
+ return total;
+
+ std::string mediaurl = templateNode->getAttributeValue("media");
+ SegmentTemplate *mediaTemplate = NULL;
+ if(mediaurl.empty() || !(mediaTemplate = new (std::nothrow) SegmentTemplate(info)) )
+ return total;
+ mediaTemplate->setSourceUrl(mediaurl);
+
+ if(templateNode->hasAttribute("startNumber"))
+ mediaTemplate->setStartIndex(Integer<uint64_t>(templateNode->getAttributeValue("startNumber")));
+
+ if(templateNode->hasAttribute("duration"))
+ mediaTemplate->duration.Set(Integer<mtime_t>(templateNode->getAttributeValue("duration")));
+
+ if(templateNode->hasAttribute("timescale"))
+ mediaTemplate->timescale.Set(Integer<uint64_t>(templateNode->getAttributeValue("timescale")));
+
+ InitSegmentTemplate *initTemplate = NULL;
+
+ if(templateNode->hasAttribute("initialization"))
+ {
+ std::string initurl = templateNode->getAttributeValue("initialization");
+ if(!initurl.empty() && (initTemplate = new (std::nothrow) InitSegmentTemplate(info)))
+ initTemplate->setSourceUrl(initurl);
+ }
+
+ info->setSegmentTemplate(mediaTemplate, SegmentInformation::INFOTYPE_MEDIA);
+ info->setSegmentTemplate(initTemplate, SegmentInformation::INFOTYPE_INIT);
+
+ total += ( mediaTemplate != NULL );
+
+ return total;
+}
+
+size_t IsoffMainParser::parseSegmentInformation(Node *node, SegmentInformation *info)
+{
+ size_t total = 0;
+ parseSegmentBase(DOMHelper::getFirstChildElementByName(node, "SegmentBase"), info);
+ total += parseSegmentList(DOMHelper::getFirstChildElementByName(node, "SegmentList"), info);
+ total += parseSegmentTemplate(DOMHelper::getFirstChildElementByName(node, "SegmentTemplate" ), info);
+ if(node->hasAttribute("bitstreamSwitching"))
+ info->setBitstreamSwitching(node->getAttributeValue("bitstreamSwitching") == "true");
+ if(node->hasAttribute("timescale"))
+ info->timescale.Set(Integer<uint64_t>(node->getAttributeValue("timescale")));
+ return total;
}
void IsoffMainParser::setAdaptationSets (Node *periodNode, Period *period)
for(it = adaptationSets.begin(); it != adaptationSets.end(); it++)
{
- AdaptationSet *adaptationSet = new AdaptationSet();
+ AdaptationSet *adaptationSet = new AdaptationSet(period);
if(!adaptationSet)
continue;
if((*it)->hasAttribute("mimeType"))
adaptationSet->setMimeType((*it)->getAttributeValue("mimeType"));
+
+ parseSegmentInformation( *it, adaptationSet );
+
setRepresentations((*it), adaptationSet);
period->addAdaptationSet(adaptationSet);
}
for(size_t i = 0; i < representations.size(); i++)
{
- this->currentRepresentation = new Representation(getMPD());
+ this->currentRepresentation = new Representation(adaptationSet, getMPD());
Node *repNode = representations.at(i);
std::vector<Node *> baseUrls = DOMHelper::getChildElementByTagName(repNode, "BaseURL");
if(!baseUrls.empty())
currentRepresentation->setBaseUrl( new BaseUrl( baseUrls.front()->getText() ) );
+ if(repNode->hasAttribute("id"))
+ currentRepresentation->setId(repNode->getAttributeValue("id"));
+
if(repNode->hasAttribute("width"))
this->currentRepresentation->setWidth(atoi(repNode->getAttributeValue("width").c_str()));
if(repNode->hasAttribute("mimeType"))
currentRepresentation->setMimeType(repNode->getAttributeValue("mimeType"));
- this->setSegmentBase(repNode, this->currentRepresentation);
- this->setSegmentList(repNode, this->currentRepresentation);
+ size_t totalmediasegments = parseSegmentInformation(repNode, currentRepresentation);
+ if(!totalmediasegments)
+ {
+ /* unranged & segment less representation, add fake segment */
+ SegmentList *list = new SegmentList();
+ Segment *seg = new Segment(currentRepresentation);
+ if(list && seg)
+ {
+ list->addSegment(seg);
+ currentRepresentation->setSegmentList(list);
+ }
+ else
+ {
+ delete seg;
+ delete list;
+ }
+ }
+
adaptationSet->addRepresentation(this->currentRepresentation);
}
}
-void IsoffMainParser::setSegmentBase (dash::xml::Node *repNode, Representation *rep)
+void IsoffMainParser::parseSegmentBase(Node * segmentBaseNode, SegmentInformation *info)
{
- std::vector<Node *> segmentBase = DOMHelper::getElementByTagName(repNode, "SegmentBase", false);
+ if(!segmentBaseNode)
+ return;
- if(segmentBase.front()->hasAttribute("indexRange"))
+ else if(segmentBaseNode->hasAttribute("indexRange"))
{
SegmentList *list = new SegmentList();
Segment *seg;
size_t start = 0, end = 0;
- if (std::sscanf(segmentBase.front()->getAttributeValue("indexRange").c_str(), "%"PRIu64"-%"PRIu64, &start, &end) == 2)
+ if (std::sscanf(segmentBaseNode->getAttributeValue("indexRange").c_str(), "%zu-%zu", &start, &end) == 2)
{
- seg = new IndexSegment(rep);
+ seg = new IndexSegment(info);
seg->setByteRange(start, end);
list->addSegment(seg);
/* index must be before data, so data starts at index end */
- seg = new Segment(rep);
+ seg = new Segment(info);
seg->setByteRange(end + 1, 0);
}
else
{
- seg = new Segment(rep);
+ seg = new Segment(info);
}
list->addSegment(seg);
- rep->setSegmentList(list);
+ info->setSegmentList(list);
- std::vector<Node *> initSeg = DOMHelper::getElementByTagName(segmentBase.front(), "Initialization", false);
+ std::vector<Node *> initSeg = DOMHelper::getElementByTagName(segmentBaseNode, "Initialization", false);
if(!initSeg.empty())
{
SegmentBase *base = new SegmentBase();
- setInitSegment(segmentBase.front(), base);
- rep->setSegmentBase(base);
+ setInitSegment(segmentBaseNode, base);
+ info->setSegmentBase(base);
}
}
- else if(!segmentBase.empty())
+ else
{
SegmentBase *base = new SegmentBase();
- setInitSegment(segmentBase.front(), base);
- rep->setSegmentBase(base);
+ setInitSegment(segmentBaseNode, base);
+ info->setSegmentBase(base);
}
}
-void IsoffMainParser::setSegmentList (dash::xml::Node *repNode, Representation *rep)
-{
- std::vector<Node *> segmentList = DOMHelper::getElementByTagName(repNode, "SegmentList", false);
- if(segmentList.size() > 0)
+size_t IsoffMainParser::parseSegmentList(Node * segListNode, SegmentInformation *info)
+{
+ size_t total = 0;
+ if(segListNode)
{
- SegmentList *list = new SegmentList();
- this->setSegments(segmentList.at(0), list);
- rep->setSegmentList(list);
- }
+ std::vector<Node *> segments = DOMHelper::getElementByTagName(segListNode, "SegmentURL", false);
+ SegmentList *list;
+ if(!segments.empty() && (list = new (std::nothrow) SegmentList()))
+ {
+ std::vector<Node *>::const_iterator it;
+ for(it = segments.begin(); it != segments.end(); it++)
+ {
+ Node *segmentURL = *it;
+ std::string mediaUrl = segmentURL->getAttributeValue("media");
+ if(mediaUrl.empty())
+ continue;
+
+ Segment *seg = new (std::nothrow) Segment(info);
+ if(!seg)
+ continue;
+
+ seg->setSourceUrl(segmentURL->getAttributeValue("media"));
+
+ if(segmentURL->hasAttribute("mediaRange"))
+ {
+ std::string range = segmentURL->getAttributeValue("mediaRange");
+ size_t pos = range.find("-");
+ seg->setByteRange(atoi(range.substr(0, pos).c_str()), atoi(range.substr(pos + 1, range.size()).c_str()));
+ }
+
+ list->addSegment(seg);
+ total++;
+ }
+ info->setSegmentList(list);
+ }
+ }
+ return total;
}
+
void IsoffMainParser::setInitSegment (dash::xml::Node *segBaseNode, SegmentBase *base)
{
std::vector<Node *> initSeg = DOMHelper::getElementByTagName(segBaseNode, "Initialisation", false);
base->addInitSegment(seg);
}
}
-void IsoffMainParser::setSegments (dash::xml::Node *segListNode, SegmentList *list)
-{
- std::vector<Node *> segments = DOMHelper::getElementByTagName(segListNode, "SegmentURL", false);
-
- for(size_t i = 0; i < segments.size(); i++)
- {
- Segment *seg = new Segment( this->currentRepresentation );
- seg->setSourceUrl(segments.at(i)->getAttributeValue("media"));
- if(segments.at(i)->hasAttribute("mediaRange"))
- {
- std::string range = segments.at(i)->getAttributeValue("mediaRange");
- size_t pos = range.find("-");
- seg->setByteRange(atoi(range.substr(0, pos).c_str()), atoi(range.substr(pos + 1, range.size()).c_str()));
- }
-
- list->addSegment(seg);
- }
-}
void IsoffMainParser::print ()
{
if(mpd)
static_cast<std::string>(mpd->getProfile()).c_str(),
mpd->getDuration(),
mpd->getMinBufferTime());
- msg_Dbg(p_stream, "BaseUrl=%s", mpd->getUrlSegment().c_str());
+ msg_Dbg(p_stream, "BaseUrl=%s", mpd->getUrlSegment().toString().c_str());
std::vector<Period *>::const_iterator i;
for(i = mpd->getPeriods().begin(); i != mpd->getPeriods().end(); i++)
{
- msg_Dbg(p_stream, " Period");
- std::vector<AdaptationSet *>::const_iterator j;
- for(j = (*i)->getAdaptationSets().begin(); j != (*i)->getAdaptationSets().end(); j++)
+ std::vector<std::string> debug = (*i)->toString();
+ std::vector<std::string>::const_iterator l;
+ for(l = debug.begin(); l < debug.end(); l++)
{
- msg_Dbg(p_stream, " AdaptationSet");
- std::vector<Representation *>::const_iterator k;
- for(k = (*j)->getRepresentations().begin(); k != (*j)->getRepresentations().end(); k++)
- {
- std::vector<std::string> debug = (*k)->toString();
- std::vector<std::string>::const_iterator l;
- for(l = debug.begin(); l < debug.end(); l++)
- {
- msg_Dbg(p_stream, "%s", (*l).c_str());
- }
- }
+ msg_Dbg(p_stream, "%s", (*l).c_str());
}
}
}
}
+
+IsoTime::IsoTime(const std::string &str)
+{
+ time = str_duration(str.c_str());
+}
+
+IsoTime::operator mtime_t () const
+{
+ return time;
+}
+
+/* i_year: year - 1900 i_month: 0-11 i_mday: 1-31 i_hour: 0-23 i_minute: 0-59 i_second: 0-59 */
+static int64_t vlc_timegm( int i_year, int i_month, int i_mday, int i_hour, int i_minute, int i_second )
+{
+ static const int pn_day[12+1] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
+ int64_t i_day;
+
+ if( i_year < 70 ||
+ i_month < 0 || i_month > 11 || i_mday < 1 || i_mday > 31 ||
+ i_hour < 0 || i_hour > 23 || i_minute < 0 || i_minute > 59 || i_second < 0 || i_second > 59 )
+ return -1;
+
+ /* Count the number of days */
+ i_day = 365 * (i_year-70) + pn_day[i_month] + i_mday - 1;
+#define LEAP(y) ( ((y)%4) == 0 && (((y)%100) != 0 || ((y)%400) == 0) ? 1 : 0)
+ for( int i = 70; i < i_year; i++ )
+ i_day += LEAP(1900+i);
+ if( i_month > 1 )
+ i_day += LEAP(1900+i_year);
+#undef LEAP
+ /**/
+ return ((24*i_day + i_hour)*60 + i_minute)*60 + i_second;
+}
+
+UTCTime::UTCTime(const std::string &str)
+{
+ enum { YEAR = 0, MON, DAY, HOUR, MIN, SEC, TZ };
+ int values[7] = {0};
+ std::istringstream in(str);
+
+ try
+ {
+ /* Date */
+ for(int i=YEAR;i<=DAY && !in.eof();i++)
+ {
+ if(i!=YEAR)
+ in.ignore(1);
+ in >> values[i];
+ }
+ /* Time */
+ if (!in.eof() && in.peek() == 'T')
+ {
+ for(int i=HOUR;i<=SEC && !in.eof();i++)
+ {
+ in.ignore(1);
+ in >> values[i];
+ }
+ }
+ /* Timezone */
+ if (!in.eof() && in.peek() == 'Z')
+ {
+ in.ignore(1);
+ while(!in.eof())
+ {
+ if(in.peek() == '+')
+ continue;
+ in >> values[TZ];
+ break;
+ }
+ }
+
+ time = vlc_timegm( values[YEAR] - 1900, values[MON] - 1, values[DAY],
+ values[HOUR], values[MIN], values[SEC] );
+ time += values[TZ] * 3600;
+ } catch(int) {
+ time = 0;
+ }
+}
+
+UTCTime::operator mtime_t () const
+{
+ return time;
+}