]> git.sesse.net Git - vlc/blob - modules/stream_filter/dash/mpd/IsoffMainParser.cpp
demux: dash: handle live stream numbered template
[vlc] / modules / stream_filter / dash / mpd / IsoffMainParser.cpp
1 /*
2  * IsoffMainParser.cpp
3  *****************************************************************************
4  * Copyright (C) 2010 - 2012 Klagenfurt University
5  *
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>
9  *
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.
14  *
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.
19  *
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  *****************************************************************************/
24
25 #ifdef HAVE_CONFIG_H
26 # include "config.h"
27 #endif
28
29 #include "IsoffMainParser.h"
30 #include "SegmentTemplate.h"
31 #include "SegmentInfoDefault.h"
32 #include "xml/DOMHelper.h"
33 #include <vlc_strings.h>
34 #include <vlc_stream.h>
35 #include <cstdio>
36 #include <sstream>
37
38 using namespace dash::mpd;
39 using namespace dash::xml;
40
41 IsoffMainParser::IsoffMainParser    (Node *root, stream_t *p_stream) :
42                  IMPDParser(root, NULL, p_stream, NULL)
43 {
44 }
45 IsoffMainParser::~IsoffMainParser   ()
46 {
47 }
48
49 bool    IsoffMainParser::parse              (Profile profile)
50 {
51     mpd = new MPD(p_stream, profile);
52     setMPDAttributes();
53     setMPDBaseUrl(root);
54     parsePeriods(root);
55
56     print();
57     return true;
58 }
59
60 void    IsoffMainParser::setMPDAttributes   ()
61 {
62     const std::map<std::string, std::string> attr = this->root->getAttributes();
63
64     std::map<std::string, std::string>::const_iterator it;
65
66     it = attr.find("mediaPresentationDuration");
67     if(it != attr.end())
68         this->mpd->setDuration(IsoTime(it->second));
69
70     it = attr.find("minBufferTime");
71     if(it != attr.end())
72         this->mpd->setMinBufferTime(IsoTime(it->second));
73
74     it = attr.find("type");
75     if(it != attr.end())
76         mpd->setType(it->second);
77
78     it = attr.find("availabilityStartTime");
79     if(it != attr.end())
80         mpd->setAvailabilityStartTime(UTCTime(it->second));
81
82     it = attr.find("timeShiftBufferDepth");
83         if(it != attr.end())
84             mpd->setTimeShiftBufferDepth(IsoTime(it->second));
85 }
86
87 void IsoffMainParser::parsePeriods(Node *root)
88 {
89     std::vector<Node *> periods = DOMHelper::getElementByTagName(root, "Period", false);
90     std::vector<Node *>::const_iterator it;
91
92     for(it = periods.begin(); it != periods.end(); it++)
93     {
94         Period *period = new (std::nothrow) Period(mpd);
95         if (!period)
96             continue;
97         parseSegmentInformation(*it, period);
98         if((*it)->hasAttribute("start"))
99             period->startTime.Set(IsoTime((*it)->getAttributeValue("start")));
100         setAdaptationSets(*it, period);
101         mpd->addPeriod(period);
102     }
103 }
104
105 size_t IsoffMainParser::parseSegmentTemplate(Node *templateNode, SegmentInformation *info)
106 {
107     size_t total = 0;
108     if (templateNode == NULL || !templateNode->hasAttribute("media"))
109         return total;
110
111     std::string mediaurl = templateNode->getAttributeValue("media");
112     SegmentTemplate *mediaTemplate = NULL;
113     if(mediaurl.empty() || !(mediaTemplate = new (std::nothrow) SegmentTemplate(info)) )
114         return total;
115     mediaTemplate->setSourceUrl(mediaurl);
116
117     if(templateNode->hasAttribute("startNumber"))
118     {
119         std::istringstream in(templateNode->getAttributeValue("startNumber"));
120         size_t i;
121         in >> i;
122         mediaTemplate->setStartIndex(i);
123     }
124
125     if(templateNode->hasAttribute("duration"))
126     {
127         std::istringstream in(templateNode->getAttributeValue("duration"));
128         size_t i;
129         in >> i;
130         mediaTemplate->duration.Set(i);
131     }
132
133     if(templateNode->hasAttribute("timescale"))
134     {
135         std::istringstream in(templateNode->getAttributeValue("timescale"));
136         size_t i;
137         in >> i;
138         mediaTemplate->timescale.Set(i);
139     }
140
141     InitSegmentTemplate *initTemplate = NULL;
142
143     if(templateNode->hasAttribute("initialization"))
144     {
145         std::string initurl = templateNode->getAttributeValue("initialization");
146         if(!initurl.empty() && (initTemplate = new (std::nothrow) InitSegmentTemplate(info)))
147             initTemplate->setSourceUrl(initurl);
148     }
149
150     info->setSegmentTemplate(mediaTemplate, SegmentInformation::INFOTYPE_MEDIA);
151     info->setSegmentTemplate(initTemplate, SegmentInformation::INFOTYPE_INIT);
152
153     total += ( mediaTemplate != NULL );
154
155     return total;
156 }
157
158 size_t IsoffMainParser::parseSegmentInformation(Node *node, SegmentInformation *info)
159 {
160     size_t total = 0;
161     parseSegmentBase(DOMHelper::getFirstChildElementByName(node, "SegmentBase"), info);
162     total += parseSegmentList(DOMHelper::getFirstChildElementByName(node, "SegmentList"), info);
163     total += parseSegmentTemplate(DOMHelper::getFirstChildElementByName(node, "SegmentTemplate" ), info);
164     if(node->hasAttribute("bitstreamSwitching"))
165         info->setBitstreamSwitching(node->getAttributeValue("bitstreamSwitching") == "true");
166     if(node->hasAttribute("timescale"))
167     {
168         std::istringstream in(node->getAttributeValue("timescale"));
169         uint64_t i;
170         in >> i;
171         info->timescale.Set(i);
172     }
173     return total;
174 }
175
176 void    IsoffMainParser::setAdaptationSets  (Node *periodNode, Period *period)
177 {
178     std::vector<Node *> adaptationSets = DOMHelper::getElementByTagName(periodNode, "AdaptationSet", false);
179     std::vector<Node *>::const_iterator it;
180
181     for(it = adaptationSets.begin(); it != adaptationSets.end(); it++)
182     {
183         AdaptationSet *adaptationSet = new AdaptationSet(period);
184         if(!adaptationSet)
185             continue;
186         if((*it)->hasAttribute("mimeType"))
187             adaptationSet->setMimeType((*it)->getAttributeValue("mimeType"));
188
189         parseSegmentInformation( *it, adaptationSet );
190
191         setRepresentations((*it), adaptationSet);
192         period->addAdaptationSet(adaptationSet);
193     }
194 }
195 void    IsoffMainParser::setRepresentations (Node *adaptationSetNode, AdaptationSet *adaptationSet)
196 {
197     std::vector<Node *> representations = DOMHelper::getElementByTagName(adaptationSetNode, "Representation", false);
198
199     for(size_t i = 0; i < representations.size(); i++)
200     {
201         this->currentRepresentation = new Representation(adaptationSet, getMPD());
202         Node *repNode = representations.at(i);
203
204         std::vector<Node *> baseUrls = DOMHelper::getChildElementByTagName(repNode, "BaseURL");
205         if(!baseUrls.empty())
206             currentRepresentation->setBaseUrl( new BaseUrl( baseUrls.front()->getText() ) );
207
208         if(repNode->hasAttribute("id"))
209             currentRepresentation->setId(repNode->getAttributeValue("id"));
210
211         if(repNode->hasAttribute("width"))
212             this->currentRepresentation->setWidth(atoi(repNode->getAttributeValue("width").c_str()));
213
214         if(repNode->hasAttribute("height"))
215             this->currentRepresentation->setHeight(atoi(repNode->getAttributeValue("height").c_str()));
216
217         if(repNode->hasAttribute("bandwidth"))
218             this->currentRepresentation->setBandwidth(atoi(repNode->getAttributeValue("bandwidth").c_str()));
219
220         if(repNode->hasAttribute("mimeType"))
221             currentRepresentation->setMimeType(repNode->getAttributeValue("mimeType"));
222
223         size_t totalmediasegments = parseSegmentInformation(repNode, currentRepresentation);
224         if(!totalmediasegments)
225         {
226             /* unranged & segment less representation, add fake segment */
227             SegmentList *list = new SegmentList();
228             Segment *seg = new Segment(currentRepresentation);
229             if(list && seg)
230             {
231                 list->addSegment(seg);
232                 currentRepresentation->setSegmentList(list);
233             }
234             else
235             {
236                 delete seg;
237                 delete list;
238             }
239         }
240
241         adaptationSet->addRepresentation(this->currentRepresentation);
242     }
243 }
244
245 void IsoffMainParser::parseSegmentBase(Node * segmentBaseNode, SegmentInformation *info)
246 {
247     if(!segmentBaseNode)
248         return;
249
250     else if(segmentBaseNode->hasAttribute("indexRange"))
251     {
252         SegmentList *list = new SegmentList();
253         Segment *seg;
254
255         size_t start = 0, end = 0;
256         if (std::sscanf(segmentBaseNode->getAttributeValue("indexRange").c_str(), "%zu-%zu", &start, &end) == 2)
257         {
258             seg = new IndexSegment(info);
259             seg->setByteRange(start, end);
260             list->addSegment(seg);
261             /* index must be before data, so data starts at index end */
262             seg = new Segment(info);
263             seg->setByteRange(end + 1, 0);
264         }
265         else
266         {
267             seg = new Segment(info);
268         }
269
270         list->addSegment(seg);
271         info->setSegmentList(list);
272
273         std::vector<Node *> initSeg = DOMHelper::getElementByTagName(segmentBaseNode, "Initialization", false);
274         if(!initSeg.empty())
275         {
276             SegmentBase *base = new SegmentBase();
277             setInitSegment(segmentBaseNode, base);
278             info->setSegmentBase(base);
279         }
280     }
281     else
282     {
283         SegmentBase *base = new SegmentBase();
284         setInitSegment(segmentBaseNode, base);
285         info->setSegmentBase(base);
286     }
287 }
288
289 size_t IsoffMainParser::parseSegmentList(Node * segListNode, SegmentInformation *info)
290 {
291     size_t total = 0;
292     if(segListNode)
293     {
294         std::vector<Node *> segments = DOMHelper::getElementByTagName(segListNode, "SegmentURL", false);
295         SegmentList *list;
296         if(!segments.empty() && (list = new (std::nothrow) SegmentList()))
297         {
298             std::vector<Node *>::const_iterator it;
299             for(it = segments.begin(); it != segments.end(); it++)
300             {
301                 Node *segmentURL = *it;
302                 std::string mediaUrl = segmentURL->getAttributeValue("media");
303                 if(mediaUrl.empty())
304                     continue;
305
306                 Segment *seg = new (std::nothrow) Segment(info);
307                 if(!seg)
308                     continue;
309
310                 seg->setSourceUrl(segmentURL->getAttributeValue("media"));
311
312                 if(segmentURL->hasAttribute("mediaRange"))
313                 {
314                     std::string range = segmentURL->getAttributeValue("mediaRange");
315                     size_t pos = range.find("-");
316                     seg->setByteRange(atoi(range.substr(0, pos).c_str()), atoi(range.substr(pos + 1, range.size()).c_str()));
317                 }
318
319                 list->addSegment(seg);
320                 total++;
321             }
322
323             info->setSegmentList(list);
324         }
325     }
326     return total;
327 }
328
329 void    IsoffMainParser::setInitSegment     (dash::xml::Node *segBaseNode, SegmentBase *base)
330 {
331     std::vector<Node *> initSeg = DOMHelper::getElementByTagName(segBaseNode, "Initialisation", false);
332
333     if(initSeg.size() == 0)
334         initSeg = DOMHelper::getElementByTagName(segBaseNode, "Initialization", false);
335
336     if(initSeg.size() > 0)
337     {
338         Segment *seg = new InitSegment( currentRepresentation );
339         seg->setSourceUrl(initSeg.at(0)->getAttributeValue("sourceURL"));
340
341         if(initSeg.at(0)->hasAttribute("range"))
342         {
343             std::string range = initSeg.at(0)->getAttributeValue("range");
344             size_t pos = range.find("-");
345             seg->setByteRange(atoi(range.substr(0, pos).c_str()), atoi(range.substr(pos + 1, range.size()).c_str()));
346         }
347
348         base->addInitSegment(seg);
349     }
350 }
351
352 void    IsoffMainParser::print              ()
353 {
354     if(mpd)
355     {
356         msg_Dbg(p_stream, "MPD profile=%s mediaPresentationDuration=%ld minBufferTime=%ld",
357                 static_cast<std::string>(mpd->getProfile()).c_str(),
358                 mpd->getDuration(),
359                 mpd->getMinBufferTime());
360         msg_Dbg(p_stream, "BaseUrl=%s", mpd->getUrlSegment().toString().c_str());
361
362         std::vector<Period *>::const_iterator i;
363         for(i = mpd->getPeriods().begin(); i != mpd->getPeriods().end(); i++)
364         {
365             std::vector<std::string> debug = (*i)->toString();
366             std::vector<std::string>::const_iterator l;
367             for(l = debug.begin(); l < debug.end(); l++)
368             {
369                 msg_Dbg(p_stream, "%s", (*l).c_str());
370             }
371         }
372     }
373 }
374
375 IsoTime::IsoTime(const std::string &str)
376 {
377     time = str_duration(str.c_str());
378 }
379
380 IsoTime::operator mtime_t () const
381 {
382     return time;
383 }
384
385 /* i_year: year - 1900  i_month: 0-11  i_mday: 1-31 i_hour: 0-23 i_minute: 0-59 i_second: 0-59 */
386 static int64_t vlc_timegm( int i_year, int i_month, int i_mday, int i_hour, int i_minute, int i_second )
387 {
388     static const int pn_day[12+1] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
389     int64_t i_day;
390
391     if( i_year < 70 ||
392         i_month < 0 || i_month > 11 || i_mday < 1 || i_mday > 31 ||
393         i_hour < 0 || i_hour > 23 || i_minute < 0 || i_minute > 59 || i_second < 0 || i_second > 59 )
394         return -1;
395
396     /* Count the number of days */
397     i_day = 365 * (i_year-70) + pn_day[i_month] + i_mday - 1;
398 #define LEAP(y) ( ((y)%4) == 0 && (((y)%100) != 0 || ((y)%400) == 0) ? 1 : 0)
399     for( int i = 70; i < i_year; i++ )
400         i_day += LEAP(1900+i);
401     if( i_month > 1 )
402         i_day += LEAP(1900+i_year);
403 #undef LEAP
404     /**/
405     return ((24*i_day + i_hour)*60 + i_minute)*60 + i_second;
406 }
407
408 UTCTime::UTCTime(const std::string &str)
409 {
410     enum { YEAR = 0, MON, DAY, HOUR, MIN, SEC, TZ };
411     int values[7] = {0};
412     std::istringstream in(str);
413
414     try
415     {
416         /* Date */
417         for(int i=YEAR;i<=DAY && !in.eof();i++)
418         {
419             if(i!=YEAR)
420                 in.ignore(1);
421             in >> values[i];
422         }
423         /* Time */
424         if (!in.eof() && in.peek() == 'T')
425         {
426             for(int i=HOUR;i<=SEC && !in.eof();i++)
427             {
428                 in.ignore(1);
429                 in >> values[i];
430             }
431         }
432         /* Timezone */
433         if (!in.eof() && in.peek() == 'Z')
434         {
435             in.ignore(1);
436             while(!in.eof())
437             {
438                 if(in.peek() == '+')
439                     continue;
440                 in >> values[TZ];
441                 break;
442             }
443         }
444
445         time = vlc_timegm( values[YEAR] - 1900, values[MON] - 1, values[DAY],
446                            values[HOUR], values[MIN], values[SEC] );
447         time += values[TZ] * 3600;
448     } catch(int) {
449         time = 0;
450     }
451 }
452
453 UTCTime::operator mtime_t () const
454 {
455     return time;
456 }