Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/VLC/modules/demux/hls/playlist/   (VLC Media Plyer ©)  Datei vom 23.9.2026 mit Größe 30 kB image not shown  

Quelle  Parser.cpp   Sprache: C

 

/*
 * Parser.cpp
 *****************************************************************************
 * Copyright © 2015 - VideoLAN and VLC Authors
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation; either version 2.1 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/

#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include "Parser.hpp"
#include "HLSSegment.hpp"
#include "HLSRepresentation.hpp"
#include "../../adaptive/SharedResources.hpp"
#include "../../adaptive/playlist/BasePeriod.h"
#include "../../adaptive/playlist/BaseAdaptationSet.h"
#include "../../adaptive/playlist/SegmentList.h"
#include "../../adaptive/encryption/Keyring.hpp"
#include "../../adaptive/tools/Retrieve.hpp"
#include "../../adaptive/tools/Helper.h"
#include "../../adaptive/tools/Conversions.hpp"
#include "../../adaptive/tools/FormatNamespace.hpp"
#include "M3U8.hpp"
#include "Tags.hpp"

#include <vlc_strings.h>
#include <vlc_stream.h>
#include <cstdio>
#include <sstream>
#include <array>
#include <unordered_map>
#include <cctype>
#include <algorithm>
#include <limits>

using namespace adaptive;
using namespace adaptive::playlist;
using namespace hls::playlist;

M3U8Parser::M3U8Parser(SharedResources *res)
{
    resources = res;
}

M3U8Parser::~M3U8Parser   ()
{
}

static std::list<Tag *> getTagsFromList(std::list<Tag *> &list, int tag)
{
    std::list<Tag *> ret;
    std::list<Tag *>::const_iterator it;
    for(it = list.begin(); it != list.end(); ++it)
    {
        if( (*it)->getType() == tag )
            ret.push_back(*it);
    }
    return ret;
}

static Tag * getTagFromList(std::list<Tag *> &list, int tag)
{
    std::list<Tag *>::const_iterator it;
    for(it = list.begin(); it != list.end(); ++it)
    {
        if( (*it)->getType() == tag )
            return *it;
    }
    return nullptr;
}

static void releaseTagsList(std::list<Tag *> &list)
{
    std::list<Tag *>::const_iterator it;
    for(it = list.begin(); it != list.end(); ++it)
        delete *it;
    list.clear();
}

HLSRepresentation * M3U8Parser::createRepresentation(BaseAdaptationSet *adaptSet, const AttributesTag * tag)
{
    const Attribute *uriAttr = tag->getAttributeByName("URI");
    const Attribute *bwAttr = tag->getAttributeByName("AVERAGE-BANDWIDTH");
    if(!bwAttr)
        bwAttr = tag->getAttributeByName("BANDWIDTH");
    const Attribute *resAttr = tag->getAttributeByName("RESOLUTION");

    HLSRepresentation *rep = new (std::nothrow) HLSRepresentation(adaptSet);
    if(rep)
    {
        rep->addAttribute(new TimescaleAttr(Timescale(CLOCK_FREQ)));

        if(uriAttr)
        {
            std::string uri;
            if(tag->getType() == AttributesTag::EXTXMEDIA)
            {
                uri = uriAttr->quotedString();
            }
            else
            {
                uri = uriAttr->value;
            }
            rep->setID(uri);
            rep->setPlaylistUrl(uri);
            if(uri.find('/') != std::string::npos)
            {
                uri = Helper::getDirectoryPath(uri);
                if(!uri.empty())
                    rep->baseUrl = new Url(uri.append("/"));
            }
        }

        if(bwAttr)
            rep->setBandwidth(bwAttr->decimal());

        if(resAttr)
        {
            std::pair<int, int> res = resAttr->getResolution();
            if(res.first && res.second)
            {
                rep->setWidth(res.first);
                rep->setHeight(res.second);
            }
        }

        const Attribute *rateAttr = tag->getAttributeByName("FRAME-RATE");
        if(rateAttr)
        {
            unsigned num, den;
            vlc_ureduce(&num, &den, rateAttr->floatingPoint() * 1000, 1000, 0);
            rep->setFrameRate(Rate(num, den));
        }
    }

    return rep;
}

void M3U8Parser::createAndFillRepresentation(vlc_object_t *p_obj, BaseAdaptationSet *adaptSet,
                                             const AttributesTag *tag,
                                             const std::list<Tag *> &tagslist)
{
    HLSRepresentation *rep  = createRepresentation(adaptSet, tag);
    if(rep)
    {
        parseSegments(p_obj, rep, tagslist);
        adaptSet->addRepresentation(rep);
    }
}

void M3U8Parser::fillRepresentationFromMediainfo(const AttributesTag *mediatag,
                                                 const std::string &type,
                                                 HLSRepresentation *rep)
{
    if(type == "AUDIO")
    {
        const Attribute *channelsAttr = mediatag->getAttributeByName("CHANNELS");
        if(channelsAttr)
            rep->setChannelsCount(std::atoi(channelsAttr->quotedString().c_str()));
    }

    if(type != "AUDIO" && type != "VIDEO" && type != "SUBTITLES")
    {
        rep->streamFormat = StreamFormat(StreamFormat::Type::Unsupported);
    }
}

void M3U8Parser::fillAdaptsetFromMediainfo(const AttributesTag *mediatag,
                                           const std::string &type,
                                           const std::string &group,
                                           BaseAdaptationSet *altAdaptSet)
{
    if(mediatag->getAttributeByName("DEFAULT"))
    {
        if(mediatag->getAttributeByName("DEFAULT")->value == "YES")
            altAdaptSet->setRole(Role(Role::Value::Main));
        else
            altAdaptSet->setRole(Role(Role::Value::Alternate));
    }

    if(mediatag->getAttributeByName("AUTOSELECT"))
    {
        if(mediatag->getAttributeByName("AUTOSELECT")->value == "NO" &&
           !mediatag->getAttributeByName("DEFAULT"))
            altAdaptSet->setRole(Role(Role::Value::Supplementary));
    }

    /* Subtitles unsupported for now */
    if(type == "SUBTITLES")
    {
        altAdaptSet->setRole(Role(Role::Value::Subtitle));
    }

    if(mediatag->getAttributeByName("LANGUAGE"))
        altAdaptSet->setLang(mediatag->getAttributeByName("LANGUAGE")->quotedString());

    std::string desc = group;
    const Attribute *nameAttr = mediatag->getAttributeByName("NAME");
    if(nameAttr)
    {
        if(!desc.empty())
            desc += " ";
        desc += nameAttr->quotedString();
    }

    if(!desc.empty())
    {
        altAdaptSet->description = desc;
        altAdaptSet->setID(ID(desc));
    }
}

bool M3U8Parser::appendSegmentsFromPlaylistURI(vlc_object_t *p_obj, HLSRepresentation *rep)
{
    block_t *p_block = Retrieve::HTTP(resources, ChunkType::Playlist, rep->getPlaylistUrl().toString());
    if(p_block)
    {
        stream_t *substream = vlc_stream_MemoryNew(p_obj, p_block->p_buffer, p_block->i_buffer, true);
        if(substream)
        {
            std::list<Tag *> tagslist = parseEntries(substream);
            vlc_stream_Delete(substream);

            parseSegments(p_obj, rep, tagslist);

            releaseTagsList(tagslist);
        }
        block_Release(p_block);
        return true;
    }
    return false;
}

static bool parseEncryption(const AttributesTag *keytag, const Url &playlistUrl,
                            CommonEncryption &encryption)
{
    if( keytag->getAttributeByName("METHOD") &&
        keytag->getAttributeByName("METHOD")->value == "AES-128" &&
        keytag->getAttributeByName("URI") )
    {
        encryption.method = CommonEncryption::Method::AES_128;
        encryption.uri.clear();

        Url keyurl(keytag->getAttributeByName("URI")->quotedString());
        if(!keyurl.hasScheme())
        {
            keyurl.prepend(Helper::getDirectoryPath(playlistUrl.toString()).append("/"));
        }

        encryption.uri = keyurl.toString();

        if(keytag->getAttributeByName("IV"))
        {
            encryption.iv.clear();
            encryption.iv = keytag->getAttributeByName("IV")->hexSequence();
        }
        return true;
    }
    else
    {
        /* unsupported or invalid */
        encryption.method = CommonEncryption::Method::None;
        encryption.uri.clear();
        encryption.iv.clear();
        return false;
    }
}

void M3U8Parser::parseSegments(vlc_object_t *, HLSRepresentation *rep, const std::list<Tag *> &tagslist)
{
    bool b_pdt = tagslist.cend() != std::find_if(tagslist.cbegin(), tagslist.cend(),
                    [](const Tag *t){return t->getType() == SingleValueTag::EXTXPROGRAMDATETIME;});
    bool b_vod = tagslist.size() && tagslist.back()->getType() == SingleValueTag::EXTXENDLIST;

    SegmentList *segmentList = new SegmentList(rep, !b_vod && !b_pdt);
    const Timescale timescale = rep->inheritTimescale();

    rep->b_loaded = true;
    rep->b_live = !b_vod;

    vlc_tick_t totalduration = 0;
    vlc_tick_t nzStartTime = 0;
    vlc_tick_t absReferenceTime = VLC_TICK_INVALID;
    uint64_t sequenceNumber = 0;
    uint64_t discontinuitySequence = 0;
    bool discontinuity = false;
    std::size_t prevbyterangeoffset = 0;
    const SingleValueTag *ctx_byterange = nullptr;
    CommonEncryption encryption;
    const ValuesListTag *ctx_extinf = nullptr;

    std::list<HLSSegment *> segmentstoappend;

    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 = nullptr;
                    ctx_byterange = nullptr;
                    break;
                }

                HLSSegment *segment = new (std::nothrow) HLSSegment(rep, sequenceNumber++);
                if(!segment)
                    break;

                segment->setSourceUrl(uritag->getValue().value);

                /* Need to use EXTXTARGETDURATION as default as some can't properly set segment one */
                vlc_tick_t nzDuration = vlc_tick_from_sec(rep->targetDuration);
                if(ctx_extinf)
                {
                    const Attribute *durAttribute = ctx_extinf->getAttributeByName("DURATION");
                    if(durAttribute)
                        nzDuration = vlc_tick_from_sec(durAttribute->floatingPoint());
                    ctx_extinf = nullptr;
                }
                segment->duration = timescale.ToScaled(nzDuration);
                segment->startTime = timescale.ToScaled(nzStartTime);
                nzStartTime += nzDuration;
                totalduration += nzDuration;
                if(absReferenceTime != VLC_TICK_INVALID)
                {
                    segment->setDisplayTime(absReferenceTime);
                    absReferenceTime += nzDuration;
                }

                segmentstoappend.push_back(segment);

                if(ctx_byterange)
                {
                    ByteRange range = ctx_byterange->getValue().getByteRange();
                    if(!range.first.has_value()) /* first == offset, second = length */
                        range.first = prevbyterangeoffset;
                    prevbyterangeoffset = *range.first + range.second;
                    segment->setByteRange(*range.first, prevbyterangeoffset - 1);
                    ctx_byterange = nullptr;
                }
                segment->setDiscontinuitySequenceNumber(discontinuitySequence);
                segment->discontinuity = discontinuity;
                discontinuity = false;

                if(encryption.method != CommonEncryption::Method::None)
                    segment->setEncryption(encryption);
            }
            break;

            case SingleValueTag::EXTXTARGETDURATION:
                rep->targetDuration = static_cast<const SingleValueTag *>(tag)->getValue().decimal();
                break;

            case SingleValueTag::EXTXPLAYLISTTYPE:
                rep->b_live = !b_vod && (static_cast<const SingleValueTag *>(tag)->getValue().value != "VOD");
                break;

            case SingleValueTag::EXTXBYTERANGE:
                ctx_byterange = static_cast<const SingleValueTag *>(tag);
                break;

            case SingleValueTag::EXTXPROGRAMDATETIME:
                absReferenceTime = VLC_TICK_0 +
                        UTCTime(static_cast<const SingleValueTag *>(tag)->getValue().value);
                /* Reverse apply UTC timespec from first discont */
                if(segmentstoappend.size() && segmentstoappend.back()->getDisplayTime() == VLC_TICK_INVALID)
                {
                    vlc_tick_t tempTime = absReferenceTime;
                    for(auto sit = segmentstoappend.crbegin(); sit != segmentstoappend.crend(); ++sit)
                    {
                        vlc_tick_t duration = timescale.ToTime((*sit)->duration);
                        if( duration < tempTime - VLC_TICK_0 )
                            tempTime -= duration;
                        else
                            tempTime = VLC_TICK_0;
                        (*sit)->setDisplayTime(tempTime);
                    }
                }
                break;

            case AttributesTag::EXTXKEY:
                parseEncryption(static_cast<const AttributesTag *>(tag),
                                rep->getPlaylistUrl(), encryption);
            break;

            case AttributesTag::EXTXMAP:
            {
                const AttributesTag *keytag = static_cast<const AttributesTag *>(tag);
                const Attribute *uriAttr;
                if(keytag && (uriAttr = keytag->getAttributeByName("URI")) &&
                   !segmentList->initialisationSegment) /* 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 ByteRange range = byterangeAttr->unescapeQuotes().getByteRange();
                            initSegment->setByteRange(range.first.value_or(0), range.first.value_or(0) + range.second - 1);
                        }
                        segmentList->initialisationSegment = initSegment;
                    }
                }
            }
            break;

            case SingleValueTag::EXTXDISCONTINUITYSEQUENCE:
                discontinuitySequence = static_cast<const SingleValueTag *>(tag)->getValue().decimal();
                break;

            case Tag::EXTXDISCONTINUITY:
                discontinuity = true;
                discontinuitySequence++;
                break;

            case Tag::EXTXENDLIST:
                break;
        }
    }

    for(HLSSegment *seg : segmentstoappend)
        segmentList->addSegment(seg);
    segmentstoappend.clear();

    if(rep->isLive())
    {
        rep->getPlaylist()->duration = 0;
    }
    else if(totalduration > rep->getPlaylist()->duration)
    {
        rep->getPlaylist()->duration = totalduration;
    }

    rep->updateSegmentList(segmentList, true);
}
M3U8 * M3U8Parser::parse(vlc_object_t *p_object, stream_t *p_stream, const std::string &playlisturl)
{
    char *psz_line = vlc_stream_ReadLine(p_stream);
    if(!psz_line || strncmp(psz_line, "#EXTM3U", 7) ||
       (psz_line[7] && !std::isspace(psz_line[7])))
    {
        free(psz_line);
        return nullptr;
    }
    free(psz_line);

    M3U8 *playlist = new (std::nothrow) M3U8(p_object);
    if(!playlist)
        return nullptr;

    if(!playlisturl.empty())
        playlist->setPlaylistUrl( Helper::getDirectoryPath(playlisturl).append("/") );

    BasePeriod *period = new (std::nothrow) BasePeriod( playlist );
    if(!period)
        return playlist;

    std::list<Tag *> tagslist = parseEntries(p_stream);
    std::list<Tag *> streaminfotags = getTagsFromList(tagslist, AttributesTag::EXTXSTREAMINF);
    bool b_masterplaylist = !streaminfotags.empty();
    if(b_masterplaylist)
    {
        std::list<Tag *>::const_iterator it;
        std::list<BaseAdaptationSet *> setstoadd;

        /* Preload Session Key */
        Tag *sessionKey = getTagFromList(tagslist, AttributesTag::EXTXSESSIONKEY);
        if(sessionKey)
        {
            CommonEncryption sessionEncryption;
            if(parseEncryption(static_cast<const AttributesTag *>(sessionKey),
                                playlist->getUrlSegment(), sessionEncryption) &&
               !sessionEncryption.uri.empty())
            {
                resources->getKeyring()->getKey(resources, sessionEncryption.uri);
            }
        }

        /* We'll need to create an adaptation set for each media group / alternative rendering
         * we create a list of playlist being and alternative/group */

        struct typecat_s
        {
            const char *type;
            es_format_category_e cat;
        };
        std::array<struct typecat_s, 3> const typescats =
        {{
            { "AUDIO",      AUDIO_ES },
            { "VIDEO",      VIDEO_ES },
            { "SUBTITLES",  SPU_ES },
        }};

        struct StreamCodec
        {
            std::string codec;
            es_format_category_e cat;
        };

        struct StreamInfos
        {
            const AttributesTag *tag;
            std::string uri;
            HLSRepresentation *rep;
            std::list<struct StreamCodec> codecs;
        };
        std::list<struct StreamInfos> streaminfolist;

        struct MediaInfos
        {
            const AttributesTag *tag;
            std::string uri;
            std::string group;
        };

        using CodecStats = std::unordered_map<std::string, unsigned>;

        std::unordered_map<std::string, CodecStats> groupsmap;
        std::list<struct MediaInfos> mediainfos;

        /* create group info */
        std::list<Tag *> mediainfotags = getTagsFromList(tagslist, AttributesTag::EXTXMEDIA);
        for(it = mediainfotags.begin(); it != mediainfotags.end(); ++it)
        {
            AttributesTag *tag = dynamic_cast<AttributesTag *>(*it);
            if(!tag)
                continue;
            const Attribute *groupid = tag->getAttributeByName("GROUP-ID");
            if(!groupid) /* invalid */
                continue;
            const Attribute *uri = tag->getAttributeByName("URI");
            MediaInfos entry;
            entry.tag = tag;
            entry.uri = uri ? uri->quotedString() : std::string();
            entry.group = groupid->quotedString();
            groupsmap.insert(std::pair<std::string, CodecStats>(entry.group, CodecStats()));
            mediainfos.push_back(entry);
        }

        /* Gather info from EXT-X-STREAMINF */
        for(it = streaminfotags.begin(); it != streaminfotags.end(); ++it)
        {
            AttributesTag *tag = dynamic_cast<AttributesTag *>(*it);
            if(!tag)
                continue;

            const Attribute *uri = tag->getAttributeByName("URI");
            if(!uri)
                continue;

            StreamInfos entry;
            entry.tag = tag;
            entry.uri = uri->quotedString();
            entry.rep = nullptr;

            const Attribute *codecsAttr = tag->getAttributeByName("CODECS");
            if(codecsAttr)
            {
                auto codecs = Helper::tokenize(codecsAttr->quotedString(), ',');
                for(auto codec : codecs)
                {
                    FormatNamespace fns(codec);
                    struct StreamCodec s;
                    s.cat = fns.getFmt()->i_cat;
                    s.codec = codec;
                    entry.codecs.push_front(s);
                }

                /* create codec reference count info per group */
                std::list<std::string> mediasCodecs;
                for(auto typecat : typescats)
                {
                    if(tag->getAttributeByName(typecat.type))
                    {
                        for(auto & codec : entry.codecs)
                        {
                            if(codec.cat == typecat.cat)
                            {
                                auto mit = groupsmap.find(tag->getAttributeByName(typecat.type)->quotedString());
                                if(mit != groupsmap.cend())
                                {
                                    auto eit = (*mit).second.find(codec.codec);
                                    if(eit != (*mit).second.end())
                                        ++(*eit).second;
                                    else
                                        (*mit).second.insert(std::pair<std::string, unsigned>(codec.codec, 0));
                                    mediasCodecs.push_front(codec.codec);
                                }
                                break;
                            }
                        }
                    }
                }

                /* remove most frequent group codecs from streaminfo */
                for(auto codec : mediasCodecs)
                    entry.codecs.remove_if([codec, entry](struct StreamCodec &v)
                                        { return v.codec == codec && entry.codecs.size() > 1; });

                /* deduplicate codecs by category as variants can have different profile */
                entry.codecs.sort([](const struct StreamCodec &a, const struct StreamCodec &b)
                    { return a.cat < b.cat ; });
                entry.codecs.unique([](const struct StreamCodec &a, const struct StreamCodec &b)
                    { return a.cat == b.cat ; });
            }

            streaminfolist.push_back(entry);
        }

        /* process all EXT-X-STREAMINF and add them */
        BaseAdaptationSet *adaptSet = new (std::nothrow) BaseAdaptationSet(period);
        if(adaptSet)
        {
            /* adaptSet->setSegmentAligned(true); FIXME: based on streamformat */
            for(auto &info : streaminfolist)
            {
                if(info.uri.empty())
                    continue;

                HLSRepresentation *rep  = createRepresentation(adaptSet, info.tag);
                if(!rep)
                    continue;

                for(auto codec: info.codecs)
                    rep->addCodecs(codec.codec);

                if(adaptSet->description.empty() &&
                   info.tag->getAttributeByName("NAME"))
                {
                    adaptSet->description = info.tag->getAttributeByName("NAME")->quotedString();
                }

                adaptSet->addRepresentation(rep);
                info.rep = rep;
            }

            if(adaptSet->getRepresentations().empty())
            {
                delete adaptSet;
                adaptSet = nullptr;
            }
            else setstoadd.push_front(adaptSet);
        }

        /* Finally add all EXT-X-MEDIA or propagate their attributes */
        for(auto mediainfo : mediainfos)
        {
            const Attribute *typeattr = mediainfo.tag->getAttributeByName("TYPE");
            if(!typeattr)
                continue;
            const std::string &mediatype = typeattr->value;

            const StreamInfos *matchedstreaminf = nullptr;
            if(!mediainfo.uri.empty())
            {
                auto sit = std::find_if(streaminfolist.begin(), streaminfolist.end(),
                                    [mediainfo] (StreamInfos &si)
                                   { return si.uri == mediainfo.uri; });
                if(sit != streaminfolist.end())
                    matchedstreaminf = & (*sit);
            };

            if(mediainfo.uri.empty() || matchedstreaminf) /* Attributes do apply to group STREAMINF members */
            {
                if(mediatype == "AUDIO" || mediatype == "VIDEO")
                for(StreamInfos &si : streaminfolist)
                {
                    if(matchedstreaminf && matchedstreaminf != &si)
                        continue;
                    const Attribute *groupattr = si.tag->getAttributeByName(mediatype.c_str());
                    if(groupattr && groupattr->quotedString() == mediainfo.group)
                    {
                        if(si.rep)
                        {
                            fillAdaptsetFromMediainfo(mediainfo.tag, typeattr->value,
                                                      mediainfo.group, si.rep->getAdaptationSet());
                            if(!matchedstreaminf || matchedstreaminf == &si)
                                fillRepresentationFromMediainfo(mediainfo.tag, typeattr->value, si.rep);
                        }
                    }
                }
            }
            else /* This is an alternative in the group */
            {
                BaseAdaptationSet *altAdaptSet = new (std::nothrow) BaseAdaptationSet(period);
                if(altAdaptSet)
                {
                    fillAdaptsetFromMediainfo(mediainfo.tag, typeattr->value, mediainfo.group, altAdaptSet);

                    HLSRepresentation *rep  = createRepresentation(altAdaptSet, mediainfo.tag);
                    if(!rep)
                    {
                        delete altAdaptSet;
                        continue;
                    }

                    fillRepresentationFromMediainfo(mediainfo.tag, typeattr->value, rep);

                    /* assign group codecs to adaptset */
                    auto groupmapit = groupsmap.find(mediainfo.group);
                    if(groupmapit != groupsmap.end())
                    {
                        for(auto & p : (*groupmapit).second)
                        {
                            FormatNamespace fns(p.first);
                            for(auto typecat : typescats)
                            {
                                if((fns.getFmt()->i_cat == typecat.cat && typeattr->value == typecat.type))
                                {
                                    rep->addCodecs(p.first);
                                    break;
                                }
                            }
                        }
                    }

                    altAdaptSet->addRepresentation(rep);
                    setstoadd.push_front(altAdaptSet);
                }
            }
        }

        /* late add to keep it ordered */
        unsigned set_id = 1;
        for(auto set : setstoadd)
        {
            if(!set->getID().isValid())
                set->setID(ID(set_id++));
            period->addAdaptationSet(set);
        }
    }
    else /* Non master playlist (opened directly subplaylist or HLS v1) */
    {
        BaseAdaptationSet *adaptSet = new (std::nothrow) BaseAdaptationSet(period);
        if(adaptSet)
        {
            AttributesTag *tag = new AttributesTag(AttributesTag::EXTXSTREAMINF, "");
            tag->addAttribute(new Attribute("URI", playlisturl));
            createAndFillRepresentation(p_object, adaptSet, tag, tagslist);
            if(!adaptSet->getRepresentations().empty())
            {
                adaptSet->getRepresentations().front()->
                    scheduleNextUpdate(std::numeric_limits<uint64_t>::max(), true);
                period->addAdaptationSet(adaptSet);
            }
            else
                delete adaptSet;
            delete tag;
        }
    }

    playlist->addPeriod(period);

    auto xstart = std::find_if(tagslist.cbegin(), tagslist.cend(),
                               [](const Tag * t) {return t->getType() == AttributesTag::EXTXSTART;});
    if(xstart != tagslist.end())
    {
        auto xstartTag = static_cast<const AttributesTag *>(*xstart);
        if(xstartTag->getAttributeByName("TIME-OFFSET"))
        {
            float offset = xstartTag->getAttributeByName("TIME-OFFSET")->floatingPoint();
            if(offset > 0 && vlc_tick_from_sec(offset) <= playlist->duration)
                playlist->presentationStartOffset = CLOCK_FREQ * offset;
            else if(offset < 0 && vlc_tick_from_sec(-offset) <= playlist->duration)
                playlist->presentationStartOffset = playlist->duration +
                                                    CLOCK_FREQ * offset;
        }
    }

    releaseTagsList(tagslist);

    playlist->debug();
    return playlist;
}

std::list<Tag *> M3U8Parser::parseEntries(stream_t *stream)
{
    std::list<Tag *> entrieslist;
    Tag *lastTag = nullptr;
    char *psz_line;

    while((psz_line = vlc_stream_ReadLine(stream)))
    {
        if(*psz_line == '#')
        {
            if(!strncmp(psz_line, "#EXT", 4)) //tag
            {
                std::string key;
                std::string attributes;
                const char *split = strchr(psz_line, ':');
                if(split)
                {
                    key = std::string(psz_line + 1, split - psz_line - 1);
                    attributes = std::string(split + 1);
                }
                else
                {
                    key = std::string(psz_line + 1);
                }

                if(!key.empty())
                {
                    Tag *tag = TagFactory::createTagByName(key, attributes);
                    if(tag)
                        entrieslist.push_back(tag);
                    lastTag = tag;
                }
            }
        }
        else if(*psz_line)
        {
            /* URI */
            if(lastTag && lastTag->getType() == AttributesTag::EXTXSTREAMINF)
            {
                AttributesTag *streaminftag = static_cast<AttributesTag *>(lastTag);
                /* master playlist uri, merge as attribute */
                Attribute *uriAttr = new (std::nothrow) Attribute("URI", std::string(psz_line));
                if(uriAttr)
                    streaminftag->addAttribute(uriAttr);
            }
            else /* playlist tag, will take modifiers */
            {
                Tag *tag = TagFactory::createTagByName("", std::string(psz_line));
                if(tag)
                    entrieslist.push_back(tag);
            }
            lastTag = nullptr;
        }
        else // drop
        {
            lastTag = nullptr;
        }

        free(psz_line);
    }

    return entrieslist;
}

Messung
CHZG
849610093

¤ Dauer der Verarbeitung: 0.13 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover


Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.