]> git.sesse.net Git - vlc/blobdiff - modules/stream_filter/dash/mpd/IsoffMainParser.cpp
demux: dash: simplify integer parsing using templates
[vlc] / modules / stream_filter / dash / mpd / IsoffMainParser.cpp
index 8329990f567b18dd9e12215567ccb2006ec79fd1..8da7adf82112b89ce13317f617c82d42c18b908f 100644 (file)
 #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;
@@ -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<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)
 {
     std::vector<Node *> adaptationSets = DOMHelper::getElementByTagName(periodNode, "AdaptationSet", false);
+    std::vector<Node *>::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,9 +178,16 @@ void    IsoffMainParser::setRepresentations (Node *adaptationSetNode, Adaptation
 
     for(size_t i = 0; i < representations.size(); i++)
     {
-        this->currentRepresentation = new Representation;
+        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()));
 
@@ -96,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<Node *> segmentBase = DOMHelper::getElementByTagName(repNode, "SegmentBase", false);
+    if(!segmentBaseNode)
+        return;
 
-    if(segmentBase.size() > 0)
+    else if(segmentBaseNode->hasAttribute("indexRange"))
+    {
+        SegmentList *list = new SegmentList();
+        Segment *seg;
+
+        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<Node *> 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<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);
@@ -133,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"))
@@ -143,34 +325,10 @@ void    IsoffMainParser::setInitSegment     (dash::xml::Node *segBaseNode, Segme
             seg->setByteRange(atoi(range.substr(0, pos).c_str()), atoi(range.substr(pos + 1, range.size()).c_str()));
         }
 
-        for(size_t i = 0; i < this->mpd->getBaseUrls().size(); i++)
-            seg->addBaseUrl(this->mpd->getBaseUrls().at(i));
-
         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()));
-        }
-
-        for(size_t j = 0; j < this->mpd->getBaseUrls().size(); j++)
-            seg->addBaseUrl(this->mpd->getBaseUrls().at(j));
-
-        list->addSegment(seg);
-    }
-}
 void    IsoffMainParser::print              ()
 {
     if(mpd)
@@ -179,31 +337,100 @@ void    IsoffMainParser::print              ()
                 static_cast<std::string>(mpd->getProfile()).c_str(),
                 mpd->getDuration(),
                 mpd->getMinBufferTime());
-        std::vector<BaseUrl *>::const_iterator h;
-        for(h = mpd->getBaseUrls().begin(); h != mpd->getBaseUrls().end(); h++)
-            msg_Dbg(p_stream, "BaseUrl=%s", (*h)->getUrl().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().begin(); k++)
-                {
-                    msg_Dbg(p_stream, "   Representation");
-                    msg_Dbg(p_stream, "    InitSeg url=%s", (*k)->getSegmentBase()->getInitSegment()->getSourceUrl().c_str());
-                    std::vector<Segment *>::const_iterator l;
-                    for(l = (*k)->getSegmentList()->getSegments().begin();
-                        l < (*k)->getSegmentList()->getSegments().end(); l++)
-                    {
-                        msg_Dbg(p_stream, "    Segment url=%s", (*l)->getSourceUrl().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;
+}