size_t IsoffMainParser::parseSegmentList(Node * segListNode, SegmentInformation *info) { size_t total = 0; if(segListNode) { std::vector<Node *> segments = DOMHelper::getElementByTagName(segListNode, "SegmentURL", false); SegmentList *list; if((list = new (std::nothrow) SegmentList(info))) { parseInitSegment(DOMHelper::getFirstChildElementByName(segListNode, "Initialization"), list, info); if(segListNode->hasAttribute("duration")) list->duration.Set(Integer<stime_t>(segListNode->getAttributeValue("duration"))); if(segListNode->hasAttribute("timescale")) list->timescale.Set(Integer<uint64_t>(segListNode->getAttributeValue("timescale"))); uint64_t nzStartTime = 0; std::vector<Node *>::const_iterator it; for(it = segments.begin(); it != segments.end(); ++it) { Node *segmentURL = *it; Segment *seg = new (std::nothrow) Segment(info); if(!seg) continue; std::string mediaUrl = segmentURL->getAttributeValue("media"); if(!mediaUrl.empty()) seg->setSourceUrl(mediaUrl); 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())); } if(list->duration.Get()) { seg->startTime.Set(nzStartTime); seg->duration.Set(list->duration.Get()); nzStartTime += list->duration.Get(); } seg->setSequenceNumber(total); list->addSegment(seg); total++; } info->setSegmentList(list); } } return total; }
size_t IsoffMainParser::parseSegmentBase(Node * segmentBaseNode, SegmentInformation *info) { size_t list_count = 0; if(!segmentBaseNode) return 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) { IndexSegment *index = new DashIndexSegment(info); index->setByteRange(start, end); list->indexSegment.Set(index); /* 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_count++; list->addSegment(seg); info->setSegmentList(list); Node *initSeg = DOMHelper::getFirstChildElementByName(segmentBaseNode, "Initialization"); if(initSeg) { SegmentBase *base = new SegmentBase(); parseInitSegment(initSeg, base, info); info->setSegmentBase(base); } } else { SegmentBase *base = new SegmentBase(); parseInitSegment(DOMHelper::getFirstChildElementByName(segmentBaseNode, "Initialization"), base, info); info->setSegmentBase(base); } return list_count; }
void M3U8Parser::parseSegments(vlc_object_t *p_obj, Representation *rep, const std::list<Tag *> &tagslist) { SegmentList *segmentList = new (std::nothrow) SegmentList(rep); rep->setTimescale(100); rep->b_loaded = true; mtime_t totalduration = 0; mtime_t nzStartTime = 0; mtime_t absReferenceTime = VLC_TS_INVALID; uint64_t sequenceNumber = 0; bool discontinuity = false; std::size_t prevbyterangeoffset = 0; const SingleValueTag *ctx_byterange = NULL; SegmentEncryption encryption; const ValuesListTag *ctx_extinf = NULL; std::list<Tag *>::const_iterator it; for(it = tagslist.begin(); it != tagslist.end(); ++it) { const Tag *tag = *it; switch(tag->getType()) { /* using static cast as attribute type permits avoiding class check */ case SingleValueTag::EXTXMEDIASEQUENCE: { sequenceNumber = (static_cast<const SingleValueTag*>(tag))->getValue().decimal(); } break; case ValuesListTag::EXTINF: { ctx_extinf = static_cast<const ValuesListTag *>(tag); } break; case SingleValueTag::URI: { const SingleValueTag *uritag = static_cast<const SingleValueTag *>(tag); if(uritag->getValue().value.empty()) { ctx_extinf = NULL; ctx_byterange = NULL; break; } HLSSegment *segment = new (std::nothrow) HLSSegment(rep, sequenceNumber++); if(!segment) break; segment->setSourceUrl(uritag->getValue().value); if((unsigned)rep->getStreamFormat() == StreamFormat::UNKNOWN) setFormatFromExtension(rep, uritag->getValue().value); if(ctx_extinf) { if(ctx_extinf->getAttributeByName("DURATION")) { const mtime_t nzDuration = CLOCK_FREQ * ctx_extinf->getAttributeByName("DURATION")->floatingPoint(); segment->duration.Set(ctx_extinf->getAttributeByName("DURATION")->floatingPoint() * (uint64_t) rep->getTimescale()); segment->startTime.Set(rep->getTimescale().ToScaled(nzStartTime)); nzStartTime += nzDuration; totalduration += nzDuration; if(absReferenceTime > VLC_TS_INVALID) { segment->utcTime = absReferenceTime; absReferenceTime += nzDuration; } } ctx_extinf = NULL; } segmentList->addSegment(segment); if(ctx_byterange) { std::pair<std::size_t,std::size_t> range = ctx_byterange->getValue().getByteRange(); if(range.first == 0) /* first == size, second = offset */ range.first = prevbyterangeoffset; prevbyterangeoffset = range.first + range.second; segment->setByteRange(range.first, prevbyterangeoffset - 1); ctx_byterange = NULL; } if(discontinuity) { segment->discontinuity = true; discontinuity = false; } if(encryption.method != SegmentEncryption::NONE) segment->setEncryption(encryption); } break; case SingleValueTag::EXTXTARGETDURATION: rep->targetDuration = static_cast<const SingleValueTag *>(tag)->getValue().decimal(); break; case SingleValueTag::EXTXPLAYLISTTYPE: rep->b_live = (static_cast<const SingleValueTag *>(tag)->getValue().value != "VOD"); break; case SingleValueTag::EXTXBYTERANGE: ctx_byterange = static_cast<const SingleValueTag *>(tag); break; case SingleValueTag::EXTXPROGRAMDATETIME: rep->b_consistent = false; absReferenceTime = VLC_TS_0 + UTCTime(static_cast<const SingleValueTag *>(tag)->getValue().value).mtime(); break; case AttributesTag::EXTXKEY: { const AttributesTag *keytag = static_cast<const AttributesTag *>(tag); if( keytag->getAttributeByName("METHOD") && keytag->getAttributeByName("METHOD")->value == "AES-128" && keytag->getAttributeByName("URI") ) { encryption.method = SegmentEncryption::AES_128; encryption.key.clear(); Url keyurl(keytag->getAttributeByName("URI")->quotedString()); if(!keyurl.hasScheme()) { keyurl.prepend(Helper::getDirectoryPath(rep->getPlaylistUrl().toString()).append("/")); } block_t *p_block = Retrieve::HTTP(p_obj, keyurl.toString()); if(p_block) { if(p_block->i_buffer == 16) { encryption.key.resize(16); memcpy(&encryption.key[0], p_block->p_buffer, 16); } block_Release(p_block); } if(keytag->getAttributeByName("IV")) { encryption.iv.clear(); encryption.iv = keytag->getAttributeByName("IV")->hexSequence(); } } else { /* unsupported or invalid */ encryption.method = SegmentEncryption::NONE; encryption.key.clear(); encryption.iv.clear(); } } break; case AttributesTag::EXTXMAP: { const AttributesTag *keytag = static_cast<const AttributesTag *>(tag); const Attribute *uriAttr; if(keytag && (uriAttr = keytag->getAttributeByName("URI")) && !segmentList->initialisationSegment.Get()) /* FIXME: handle discontinuities */ { InitSegment *initSegment = new (std::nothrow) InitSegment(rep); if(initSegment) { initSegment->setSourceUrl(uriAttr->quotedString()); const Attribute *byterangeAttr = keytag->getAttributeByName("BYTERANGE"); if(byterangeAttr) { const std::pair<std::size_t,std::size_t> range = byterangeAttr->unescapeQuotes().getByteRange(); initSegment->setByteRange(range.first, range.first + range.second - 1); } segmentList->initialisationSegment.Set(initSegment); } } } break; case Tag::EXTXDISCONTINUITY: discontinuity = true; break; case Tag::EXTXENDLIST: rep->b_live = false; break; } } if(rep->isLive()) { rep->getPlaylist()->duration.Set(0); } else if(totalduration > rep->getPlaylist()->duration.Get()) { rep->getPlaylist()->duration.Set(totalduration); } rep->setSegmentList(segmentList); }