X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Fstream_filter%2Fdash%2Fmpd%2FIsoffMainParser.cpp;h=8da7adf82112b89ce13317f617c82d42c18b908f;hb=01e36efec5df5533adf81963633653dd7874d71f;hp=eae8f8d4d232ec346f8aaeb163b6daff3fcf8d78;hpb=7081fb2b01a5e3651eb24649db87802636e7a40f;p=vlc diff --git a/modules/stream_filter/dash/mpd/IsoffMainParser.cpp b/modules/stream_filter/dash/mpd/IsoffMainParser.cpp index eae8f8d4d2..8da7adf821 100644 --- a/modules/stream_filter/dash/mpd/IsoffMainParser.cpp +++ b/modules/stream_filter/dash/mpd/IsoffMainParser.cpp @@ -27,8 +27,13 @@ #endif #include "IsoffMainParser.h" +#include "SegmentTemplate.h" +#include "SegmentInfoDefault.h" #include "xml/DOMHelper.h" #include +#include +#include +#include using namespace dash::mpd; using namespace dash::xml; @@ -41,12 +46,13 @@ IsoffMainParser::~IsoffMainParser () { } -bool IsoffMainParser::parse () +bool IsoffMainParser::parse (Profile profile) { - mpd = new MPD(); + mpd = new MPD(p_stream, profile); setMPDAttributes(); setMPDBaseUrl(root); - setPeriods(root); + parsePeriods(root); + print(); return true; } @@ -59,22 +65,110 @@ void IsoffMainParser::setMPDAttributes () 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 periods = DOMHelper::getElementByTagName(root, "Period", false); + std::vector::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(templateNode->getAttributeValue("startNumber"))); + + if(templateNode->hasAttribute("duration")) + mediaTemplate->duration.Set(Integer(templateNode->getAttributeValue("duration"))); + + if(templateNode->hasAttribute("timescale")) + mediaTemplate->timescale.Set(Integer(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(node->getAttributeValue("timescale"))); + return total; } void IsoffMainParser::setAdaptationSets (Node *periodNode, Period *period) { std::vector adaptationSets = DOMHelper::getElementByTagName(periodNode, "AdaptationSet", false); + std::vector::const_iterator it; - for(size_t i = 0; i < adaptationSets.size(); i++) + for(it = adaptationSets.begin(); it != adaptationSets.end(); it++) { - AdaptationSet *adaptationSet = new AdaptationSet(); - this->setRepresentations(adaptationSets.at(i), 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); } } @@ -84,13 +178,16 @@ void IsoffMainParser::setRepresentations (Node *adaptationSetNode, Adaptation 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 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())); @@ -100,34 +197,115 @@ void IsoffMainParser::setRepresentations (Node *adaptationSetNode, Adaptation if(repNode->hasAttribute("bandwidth")) this->currentRepresentation->setBandwidth(atoi(repNode->getAttributeValue("bandwidth").c_str())); - this->setSegmentBase(repNode, this->currentRepresentation); - this->setSegmentList(repNode, this->currentRepresentation); + if(repNode->hasAttribute("mimeType")) + currentRepresentation->setMimeType(repNode->getAttributeValue("mimeType")); + + 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 segmentBase = DOMHelper::getElementByTagName(repNode, "SegmentBase", false); + if(!segmentBaseNode) + return; + + else if(segmentBaseNode->hasAttribute("indexRange")) + { + SegmentList *list = new SegmentList(); + Segment *seg; - if(segmentBase.size() > 0) + size_t start = 0, end = 0; + if (std::sscanf(segmentBaseNode->getAttributeValue("indexRange").c_str(), "%zu-%zu", &start, &end) == 2) + { + 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(info); + seg->setByteRange(end + 1, 0); + } + else + { + seg = new Segment(info); + } + + list->addSegment(seg); + info->setSegmentList(list); + + std::vector initSeg = DOMHelper::getElementByTagName(segmentBaseNode, "Initialization", false); + if(!initSeg.empty()) + { + SegmentBase *base = new SegmentBase(); + setInitSegment(segmentBaseNode, base); + info->setSegmentBase(base); + } + } + else { SegmentBase *base = new SegmentBase(); - this->setInitSegment(segmentBase.at(0), base); - rep->setSegmentBase(base); + setInitSegment(segmentBaseNode, base); + info->setSegmentBase(base); } } -void IsoffMainParser::setSegmentList (dash::xml::Node *repNode, Representation *rep) -{ - std::vector 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 segments = DOMHelper::getElementByTagName(segListNode, "SegmentURL", false); + SegmentList *list; + if(!segments.empty() && (list = new (std::nothrow) SegmentList())) + { + std::vector::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 initSeg = DOMHelper::getElementByTagName(segBaseNode, "Initialisation", false); @@ -137,7 +315,7 @@ void IsoffMainParser::setInitSegment (dash::xml::Node *segBaseNode, Segme if(initSeg.size() > 0) { - Segment *seg = new Segment( this->currentRepresentation ); + Segment *seg = new InitSegment( currentRepresentation ); seg->setSourceUrl(initSeg.at(0)->getAttributeValue("sourceURL")); if(initSeg.at(0)->hasAttribute("range")) @@ -150,25 +328,7 @@ void IsoffMainParser::setInitSegment (dash::xml::Node *segBaseNode, Segme base->addInitSegment(seg); } } -void IsoffMainParser::setSegments (dash::xml::Node *segListNode, SegmentList *list) -{ - std::vector 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) @@ -177,27 +337,100 @@ void IsoffMainParser::print () static_cast(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::const_iterator i; for(i = mpd->getPeriods().begin(); i != mpd->getPeriods().end(); i++) { - msg_Dbg(p_stream, " Period"); - std::vector::const_iterator j; - for(j = (*i)->getAdaptationSets().begin(); j != (*i)->getAdaptationSets().end(); j++) + std::vector debug = (*i)->toString(); + std::vector::const_iterator l; + for(l = debug.begin(); l < debug.end(); l++) { - msg_Dbg(p_stream, " AdaptationSet"); - std::vector::const_iterator k; - for(k = (*j)->getRepresentations().begin(); k != (*j)->getRepresentations().end(); k++) - { - std::vector debug = (*k)->toString(); - std::vector::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; +}