[vlc-commits] [Git][videolan/vlc][3.0.x] 8 commits: demux: adaptive: fix unitialized var
François Cartegnie (@fcartegnie)
gitlab at videolan.org
Fri Nov 4 16:42:35 UTC 2022
François Cartegnie pushed to branch 3.0.x at VideoLAN / VLC
Commits:
b607efd5 by Francois Cartegnie at 2022-11-04T16:20:45+00:00
demux: adaptive: fix unitialized var
- - - - -
f8a1f5d5 by Francois Cartegnie at 2022-11-04T16:20:45+00:00
demux: hls: prefer average bandwidth when available
(cherry picked from commit f9b0cad3896c0276d467c5ad3be104baf946acdf)
- - - - -
d8ae0f09 by Francois Cartegnie at 2022-11-04T16:20:45+00:00
demux: adaptive: codecs does not belong to ext-x-media
(cherry picked from commit 7d37ad40e40419c28bf45ecf97fd0ea47cfa7f1e)
- - - - -
4ff3adad by Francois Cartegnie at 2022-11-04T16:20:45+00:00
demux: hls: rework stream/media attributes propagation
(cherry picked from commit a54d02b0c9a5b0a2073f393a33235d9adfd6c25d)
- - - - -
ea8e33c8 by Francois Cartegnie at 2022-11-04T16:20:45+00:00
demux: hls: set channels
(cherry picked from commit c5b37cb43da4cd2507e970e05de53b24200d63bd)
- - - - -
7d9d51ac by Francois Cartegnie at 2022-11-04T16:20:45+00:00
demux: adaptive: return tracker update status
(cherry picked from commit 9d83a7ee1a6ec993e73e419fc84357cc6433c908)
- - - - -
9e50bef3 by Francois Cartegnie at 2022-11-04T16:20:45+00:00
demux: adaptive: forward update status through stream
(cherry picked from commit aeca11e9627781f05e9baee86fda2af87306e76c)
- - - - -
ee321360 by Francois Cartegnie at 2022-11-04T16:20:45+00:00
demux: adaptive: allow implementations to preparse
(cherry picked from commit 0f9cde94991d12e253c480324f95ba513e5b99b1)
- - - - -
16 changed files:
- modules/demux/adaptive/ID.cpp
- modules/demux/adaptive/ID.hpp
- modules/demux/adaptive/PlaylistManager.cpp
- modules/demux/adaptive/PlaylistManager.h
- modules/demux/adaptive/SegmentTracker.cpp
- modules/demux/adaptive/SegmentTracker.hpp
- modules/demux/adaptive/Streams.cpp
- modules/demux/adaptive/Streams.hpp
- modules/demux/adaptive/logic/NearOptimalAdaptationLogic.cpp
- modules/demux/adaptive/playlist/CodecDescription.cpp
- modules/demux/adaptive/playlist/CodecDescription.hpp
- modules/demux/adaptive/test/playlist/M3U8.cpp
- modules/demux/hls/playlist/HLSRepresentation.cpp
- modules/demux/hls/playlist/HLSRepresentation.hpp
- modules/demux/hls/playlist/Parser.cpp
- modules/demux/hls/playlist/Parser.hpp
Changes:
=====================================
modules/demux/adaptive/ID.cpp
=====================================
@@ -39,6 +39,11 @@ ID::ID(uint64_t id_)
id = ss.str();
}
+bool ID::isValid() const
+{
+ return !id.empty();
+}
+
bool ID::operator==(const ID &other) const
{
return (!id.empty() && id == other.id);
=====================================
modules/demux/adaptive/ID.hpp
=====================================
@@ -33,6 +33,7 @@ namespace adaptive
bool operator==(const ID &) const;
bool operator<(const ID &) const;
std::string str() const;
+ bool isValid() const;
private:
std::string id;
=====================================
modules/demux/adaptive/PlaylistManager.cpp
=====================================
@@ -161,6 +161,8 @@ bool PlaylistManager::init(bool b_preparsing)
playlist->playbackStart.Set(time(nullptr));
nextPlaylistupdate = playlist->playbackStart.Get();
+ if(b_preparsing)
+ preparsePlaylist();
updateControlsPosition();
return true;
@@ -422,6 +424,11 @@ bool PlaylistManager::updatePlaylist()
return true;
}
+void PlaylistManager::preparsePlaylist()
+{
+
+}
+
Times PlaylistManager::getTimes(bool b_first) const
{
vlc_mutex_locker locker(&demux.lock);
=====================================
modules/demux/adaptive/PlaylistManager.h
=====================================
@@ -66,6 +66,7 @@ namespace adaptive
virtual bool needsUpdate() const;
virtual bool updatePlaylist();
virtual void scheduleNextUpdate();
+ virtual void preparsePlaylist();
/* static callbacks */
static int control_callback(demux_t *, int, va_list);
@@ -105,6 +106,7 @@ namespace adaptive
demux_t *p_demux;
std::vector<AbstractStream *> streams;
BasePeriod *currentPeriod;
+ bool b_preparsing;
enum class TimestampSynchronizationPoint
{
@@ -151,7 +153,6 @@ namespace adaptive
bool b_buffering;
bool b_canceled;
mtime_t pause_start;
- bool b_preparsing;
};
}
=====================================
modules/demux/adaptive/SegmentTracker.cpp
=====================================
@@ -596,18 +596,22 @@ bool SegmentTracker::bufferingAvailable() const
return true;
}
-void SegmentTracker::updateSelected()
+bool SegmentTracker::updateSelected()
{
+ bool b_updated;
if(current.rep && current.rep->needsUpdate(next.number))
{
- bool b_updated = current.rep->runLocalUpdates(resources);
+ b_updated = current.rep->runLocalUpdates(resources);
current.rep->scheduleNextUpdate(current.number, b_updated);
if(b_updated)
notify(RepresentationUpdatedEvent(current.rep));
}
+ else b_updated = false;
if(current.rep && current.rep->canNoLongerUpdate())
notify(RepresentationUpdateFailedEvent(current.rep));
+
+ return b_updated;
}
void SegmentTracker::notify(const TrackerEvent &event) const
=====================================
modules/demux/adaptive/SegmentTracker.hpp
=====================================
@@ -235,7 +235,7 @@ namespace adaptive
void notifyBufferingState(bool) const;
void notifyBufferingLevel(mtime_t, mtime_t, mtime_t, mtime_t) const;
void registerListener(SegmentTrackerListenerInterface *);
- void updateSelected();
+ bool updateSelected();
bool bufferingAvailable() const;
private:
=====================================
modules/demux/adaptive/Streams.cpp
=====================================
@@ -727,10 +727,15 @@ bool AbstractStream::getMediaAdvanceAmount(mtime_t *duration) const
return true;
}
-void AbstractStream::runUpdates()
+bool AbstractStream::runUpdates(bool)
{
- if(valid && !disabled)
- segmentTracker->updateSelected();
+ if(!valid)
+ return false;
+
+ if(!disabled)
+ return segmentTracker->updateSelected();
+ else
+ return false;
}
void AbstractStream::fillExtraFMTInfo( es_format_t *p_fmt ) const
=====================================
modules/demux/adaptive/Streams.hpp
=====================================
@@ -102,7 +102,7 @@ namespace adaptive
virtual bool setPosition(const StreamPosition &, bool);
bool getMediaPlaybackTimes(mtime_t *, mtime_t *, mtime_t *) const;
bool getMediaAdvanceAmount(mtime_t *) const;
- void runUpdates();
+ bool runUpdates(bool = false);
/* Used by demuxers fake streams */
virtual block_t *readNextBlock() override;
=====================================
modules/demux/adaptive/logic/NearOptimalAdaptationLogic.cpp
=====================================
@@ -70,7 +70,7 @@ NearOptimalAdaptationLogic::getNextQualityIndex( BaseAdaptationSet *adaptSet, Re
{
BaseRepresentation *ret = nullptr;
BaseRepresentation *prev = nullptr;
- float argmax;
+ float argmax = 0;
for(BaseRepresentation *rep = selector.lowest(adaptSet);
rep && rep != prev; rep = selector.higher(adaptSet, rep))
{
=====================================
modules/demux/adaptive/playlist/CodecDescription.cpp
=====================================
@@ -93,6 +93,11 @@ void CodecDescription::setSampleRate(const Rate &r)
fmt.audio.i_rate = r.num();
}
+void CodecDescription::setChannelsCount(unsigned c)
+{
+ fmt.audio.i_channels = c;
+}
+
CodecDescriptionList::CodecDescriptionList()
{
=====================================
modules/demux/adaptive/playlist/CodecDescription.hpp
=====================================
@@ -44,6 +44,7 @@ namespace adaptive
void setAspectRatio(const AspectRatio &);
void setFrameRate(const Rate &);
void setSampleRate(const Rate &);
+ void setChannelsCount(unsigned);
protected:
es_format_t fmt;
=====================================
modules/demux/adaptive/test/playlist/M3U8.cpp
=====================================
@@ -27,6 +27,7 @@
#include "../../playlist/BaseAdaptationSet.h"
#include "../../playlist/BaseRepresentation.h"
#include "../../logic/BufferingLogic.hpp"
+#include "../../tools/FormatNamespace.hpp"
#include "../../SegmentTracker.hpp"
#include "../../../hls/playlist/Parser.hpp"
#include "../../../hls/playlist/M3U8.hpp"
@@ -125,10 +126,10 @@ int M3U8MasterPlaylist_test()
{
Expect(m3u);
Expect(m3u->getPeriods().size() == 1);
- Expect(m3u->getFirstPeriod()->getAdaptationSets().size() == 4);
+ Expect(m3u->getFirstPeriod()->getAdaptationSets().size() == 3);
BaseAdaptationSet *set = m3u->getFirstPeriod()->getAdaptationSetByID(ID("aac English"));
Expect(set);
- Expect(set->getRepresentations().size() == 1);
+ Expect(set->getRepresentations().size() == 2);
Expect(set->getLang() == "en");
Expect(set->getRole().autoSelectable());
Expect(set->getRole().isDefault());
@@ -140,6 +141,131 @@ int M3U8MasterPlaylist_test()
return 1;
}
+ /* Empty MEDIA URI property group propagation test */
+ {
+ const char srcmanifest[] =
+ "#EXTM3U\n"
+ "#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID=\"aach-64\",NAME=\"audio 64\",AUTOSELECT=YES,DEFAULT=YES,CHANNELS=\"2\"\n"
+ "#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID=\"aacl-128\",NAME=\"audio 128\",AUTOSELECT=YES,DEFAULT=YES,CHANNELS=\"2\"\n"
+ "#EXT-X-STREAM-INF:BANDWIDTH=75000,AVERAGE-BANDWIDTH=70000,CODECS=\"mp4a.40.5\",AUDIO=\"aach-64\"\n"
+ "streaminf0.m3u8\n"
+ "#EXT-X-STREAM-INF:BANDWIDTH=150000,AVERAGE-BANDWIDTH=125000,CODECS=\"mp4a.40.2\",AUDIO=\"aacl-128\"\n"
+ "streaminf1.m3u8\n"
+ ;
+
+ m3u = ParseM3U8(obj, srcmanifest, sizeof(srcmanifest));
+ try
+ {
+ Expect(m3u);
+ Expect(m3u->getFirstPeriod());
+ Expect(m3u->getFirstPeriod()->getAdaptationSets().size() == 1);
+ BaseAdaptationSet *set = m3u->getFirstPeriod()->getAdaptationSets().front();
+ Expect(set->getRole().autoSelectable());
+ Expect(set->getRole().isDefault());
+ auto reps = set->getRepresentations();
+ Expect(reps.size() == 2);
+ Expect(reps[0]->getBandwidth() == 70000);
+ Expect(reps[1]->getBandwidth() == 125000);
+
+ FormatNamespace fns("mp4a.40.5");
+ CodecDescriptionList codecsdesclist;
+ reps[0]->getCodecsDesc(&codecsdesclist);
+ Expect(codecsdesclist.size() == 1);
+ const es_format_t *fmt = codecsdesclist.front()->getFmt();
+ Expect(fmt != nullptr);
+ Expect(fmt->i_codec == fns.getFmt()->i_codec);
+ Expect(fmt->i_profile == fns.getFmt()->i_profile);
+ Expect(fmt->i_cat == AUDIO_ES);
+ Expect(fmt->audio.i_channels == 2);
+
+ delete m3u;
+ }
+ catch (...)
+ {
+ delete m3u;
+ return 1;
+ }
+ }
+
+ /* check codec assignment per group */
+ {
+ const char srcmanifest[] =
+ "#EXTM3U\n"
+ "#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID=\"Audio\",NAME=\"en_aud\",DEFAULT=YES,AUTOSELECT=YES,"
+ " LANGUAGE=\"en\",CHANNELS=\"2\",URI=\"a0.m3u8\"\n"
+ "#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID=\"Subtitles\",NAME=\"sub_fr\",DEFAULT=YES,FORCED=NO,LANGUAGE=\"fr\","
+ " URI=\"cc0.m3u8\"\n"
+ "#EXT-X-MEDIA:TYPE=CLOSED-CAPTIONS,GROUP-ID=\"Closed Captions\",NAME=\"cc_en\",DEFAULT=YES,AUTOSELECT=YES,"
+ " LANGUAGE=\"en\",INSTREAM-ID=\"CC1\"\n"
+ "#EXT-X-STREAM-INF:BANDWIDTH=629758,AVERAGE-BANDWIDTH=572508,RESOLUTION=426x240,CODECS=\"avc1.42E015,mp4a.40.2,wvtt\","
+ " FRAME-RATE=25,AUDIO=\"Audio\",SUBTITLES=\"Subtitles\",CLOSED-CAPTIONS=\"Closed Captions\"\n"
+ "v0.m3u8\n"
+ "#EXT-X-STREAM-INF:BANDWIDTH=1069758,AVERAGE-BANDWIDTH=972508,RESOLUTION=640x360,CODECS=\"avc1.4D401E,mp4a.40.2,wvtt\","
+ " FRAME-RATE=29.970,AUDIO=\"Audio\",SUBTITLES=\"Subtitles\",CLOSED-CAPTIONS=\"Closed Captions\"\n"
+ "v1.m3u8\n"
+ ;
+
+ m3u = ParseM3U8(obj, srcmanifest, sizeof(srcmanifest));
+ try
+ {
+ Expect(m3u);
+ Expect(m3u->getFirstPeriod());
+ Expect(m3u->getFirstPeriod()->getAdaptationSets().size() == 3);
+
+ {
+ auto set = m3u->getFirstPeriod()->getAdaptationSetByID(ID("Audio en_aud"));
+ Expect(set != nullptr);
+ auto reps = set->getRepresentations();
+ Expect(reps.size() == 1);
+ FormatNamespace fns("mp4a.40.2");
+ CodecDescriptionList codecsdesclist;
+ reps[0]->getCodecsDesc(&codecsdesclist);
+ Expect(codecsdesclist.size() == 1);
+ const es_format_t *fmt = codecsdesclist.front()->getFmt();
+ Expect(fmt != nullptr);
+ Expect(fmt->i_codec == fns.getFmt()->i_codec);
+ Expect(fmt->i_cat == AUDIO_ES);
+ Expect(fmt->audio.i_channels == 2);
+ }
+
+ {
+ auto set = m3u->getFirstPeriod()->getAdaptationSetByID(ID("Subtitles sub_fr"));
+ Expect(set != nullptr);
+ auto reps = set->getRepresentations();
+ Expect(reps.size() == 1);
+ FormatNamespace fns("wvtt");
+ CodecDescriptionList codecsdesclist;
+ reps[0]->getCodecsDesc(&codecsdesclist);
+ Expect(codecsdesclist.size() == 1);
+ const es_format_t *fmt = codecsdesclist.front()->getFmt();
+ Expect(fmt != nullptr);
+ Expect(fmt->i_codec == fns.getFmt()->i_codec);
+ Expect(fmt->i_cat == SPU_ES);
+ }
+
+ {
+ auto set = m3u->getFirstPeriod()->getAdaptationSetByID(ID("default_id#0"));
+ Expect(set != nullptr);
+ auto reps = set->getRepresentations();
+ Expect(reps.size() == 2);
+ FormatNamespace fns("avc1.42E015");
+ CodecDescriptionList codecsdesclist;
+ reps[0]->getCodecsDesc(&codecsdesclist);
+ Expect(codecsdesclist.size() == 1);
+ const es_format_t *fmt = codecsdesclist.front()->getFmt();
+ Expect(fmt != nullptr);
+ Expect(fmt->i_codec == fns.getFmt()->i_codec);
+ Expect(fmt->i_cat == VIDEO_ES);
+ }
+
+ delete m3u;
+ }
+ catch (...)
+ {
+ delete m3u;
+ return 1;
+ }
+ }
return 0;
}
@@ -420,6 +546,5 @@ int M3U8Playlist_test()
return 1;
}
-
return 0;
}
=====================================
modules/demux/hls/playlist/HLSRepresentation.cpp
=====================================
@@ -48,6 +48,7 @@ HLSRepresentation::HLSRepresentation ( BaseAdaptationSet *set ) :
lastUpdateTime = 0;
targetDuration = 0;
streamFormat = StreamFormat::Type::Unknown;
+ channels = 0;
}
HLSRepresentation::~HLSRepresentation ()
@@ -178,6 +179,21 @@ bool HLSRepresentation::canNoLongerUpdate() const
return updateFailureCount > MAX_UPDATE_FAILED_UPDATE_COUNT;
}
+void HLSRepresentation::setChannelsCount(unsigned c)
+{
+ channels = c;
+}
+
+CodecDescription * HLSRepresentation::makeCodecDescription(const std::string &s) const
+{
+ CodecDescription *desc = BaseRepresentation::makeCodecDescription(s);
+ if(desc)
+ {
+ desc->setChannelsCount(channels);
+ }
+ return desc;
+}
+
uint64_t HLSRepresentation::translateSegmentNumber(uint64_t num, const BaseRepresentation *from) const
{
if(targetDuration == static_cast<const HLSRepresentation *>(from)->targetDuration)
=====================================
modules/demux/hls/playlist/HLSRepresentation.hpp
=====================================
@@ -54,6 +54,9 @@ namespace hls
virtual bool canNoLongerUpdate() const override;
virtual uint64_t translateSegmentNumber(uint64_t, const BaseRepresentation *) const override;
+ virtual CodecDescription * makeCodecDescription(const std::string &) const override;
+
+ void setChannelsCount(unsigned);
protected:
time_t targetDuration;
@@ -66,6 +69,7 @@ namespace hls
bool b_loaded;
unsigned updateFailureCount;
mtime_t lastUpdateTime;
+ unsigned channels;
};
}
}
=====================================
modules/demux/hls/playlist/Parser.cpp
=====================================
@@ -32,6 +32,7 @@
#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"
@@ -39,7 +40,8 @@
#include <vlc_stream.h>
#include <cstdio>
#include <sstream>
-#include <map>
+#include <array>
+#include <unordered_map>
#include <cctype>
#include <algorithm>
#include <limits>
@@ -91,7 +93,9 @@ static void releaseTagsList(std::list<Tag *> &list)
HLSRepresentation * M3U8Parser::createRepresentation(BaseAdaptationSet *adaptSet, const AttributesTag * tag)
{
const Attribute *uriAttr = tag->getAttributeByName("URI");
- const Attribute *bwAttr = tag->getAttributeByName("BANDWIDTH");
+ 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);
@@ -121,9 +125,6 @@ HLSRepresentation * M3U8Parser::createRepresentation(BaseAdaptationSet *adaptSet
if(bwAttr)
rep->setBandwidth(bwAttr->decimal());
- if(tag->getAttributeByName("CODECS"))
- rep->addCodecs(tag->getAttributeByName("CODECS")->quotedString());
-
if(resAttr)
{
std::pair<int, int> res = resAttr->getResolution();
@@ -159,6 +160,68 @@ void M3U8Parser::createAndFillRepresentation(vlc_object_t *p_obj, BaseAdaptation
}
}
+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.Set(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());
@@ -425,11 +488,12 @@ M3U8 * M3U8Parser::parse(vlc_object_t *p_object, stream_t *p_stream, const std::
return playlist;
std::list<Tag *> tagslist = parseEntries(p_stream);
- bool b_masterplaylist = !getTagsFromList(tagslist, AttributesTag::EXTXSTREAMINF).empty();
+ std::list<Tag *> streaminfotags = getTagsFromList(tagslist, AttributesTag::EXTXSTREAMINF);
+ bool b_masterplaylist = !streaminfotags.empty();
if(b_masterplaylist)
{
std::list<Tag *>::const_iterator it;
- std::map<std::string, AttributesTag *> groupsmap;
+ std::list<BaseAdaptationSet *> setstoadd;
/* Preload Session Key */
Tag *sessionKey = getTagFromList(tagslist, AttributesTag::EXTXSESSIONKEY);
@@ -446,119 +510,255 @@ M3U8 * M3U8Parser::parse(vlc_object_t *p_object, stream_t *p_stream, const std::
/* 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 && tag->getAttributeByName("URI"))
- {
- std::pair<std::string, AttributesTag *> pair(tag->getAttributeByName("URI")->quotedString(), tag);
- groupsmap.insert(pair);
- }
+ 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);
}
- /* Then we parse all playlists uri and add them, except when alternative */
- BaseAdaptationSet *adaptSet = new (std::nothrow) BaseAdaptationSet(period);
- if(adaptSet)
+ /* Gather info from EXT-X-STREAMINF */
+ for(it = streaminfotags.begin(); it != streaminfotags.end(); ++it)
{
- /* adaptSet->setSegmentAligned(true); FIXME: based on streamformat */
- std::list<Tag *> streaminfotags = getTagsFromList(tagslist, AttributesTag::EXTXSTREAMINF);
- 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)
{
- AttributesTag *tag = dynamic_cast<AttributesTag *>(*it);
- if(tag && tag->getAttributeByName("URI"))
+ 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(groupsmap.find(tag->getAttributeByName("URI")->value) == groupsmap.end())
+ if(tag->getAttributeByName(typecat.type))
{
- /* not a group, belong to default adaptation set */
- HLSRepresentation *rep = createRepresentation(adaptSet, tag);
- if(rep)
+ for(auto codec : entry.codecs)
{
- adaptSet->addRepresentation(rep);
+ 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 ; });
}
- if(!adaptSet->getRepresentations().empty())
- period->addAdaptationSet(adaptSet);
- else
- delete adaptSet;
+
+ streaminfolist.push_back(entry);
}
- /* Finally add all groups */
- unsigned set_id = 1;
- std::map<std::string, AttributesTag *>::const_iterator groupsit;
- for(groupsit = groupsmap.begin(); groupsit != groupsmap.end(); ++groupsit)
+ /* process all EXT-X-STREAMINF and add them */
+ BaseAdaptationSet *adaptSet = new (std::nothrow) BaseAdaptationSet(period);
+ if(adaptSet)
{
- std::pair<std::string, AttributesTag *> pair = *groupsit;
- if(!pair.second->getAttributeByName("TYPE"))
- continue;
-
- BaseAdaptationSet *altAdaptSet = new (std::nothrow) BaseAdaptationSet(period);
- if(altAdaptSet)
+ /* adaptSet->setSegmentAligned(true); FIXME: based on streamformat */
+ for(auto &info : streaminfolist)
{
- HLSRepresentation *rep = createRepresentation(altAdaptSet, pair.second);
- if(rep)
- {
- altAdaptSet->addRepresentation(rep);
- }
+ if(info.uri.empty())
+ continue;
- std::string desc;
- if(pair.second->getAttributeByName("GROUP-ID"))
- desc = pair.second->getAttributeByName("GROUP-ID")->quotedString();
- if(pair.second->getAttributeByName("NAME"))
- {
- if(!desc.empty())
- desc += " ";
- desc += pair.second->getAttributeByName("NAME")->quotedString();
- }
+ HLSRepresentation *rep = createRepresentation(adaptSet, info.tag);
+ if(!rep)
+ continue;
- if(pair.second->getAttributeByName("CODECS"))
- rep->addCodecs(pair.second->getAttributeByName("CODECS")->quotedString());
+ for(auto codec: info.codecs)
+ rep->addCodecs(codec.codec);
- if(!desc.empty())
+ if(adaptSet->description.Get().empty() &&
+ info.tag->getAttributeByName("NAME"))
{
- altAdaptSet->description.Set(desc);
- altAdaptSet->setID(ID(desc));
+ adaptSet->description.Set(info.tag->getAttributeByName("NAME")->quotedString());
}
- else altAdaptSet->setID(ID(set_id++));
- if(pair.second->getAttributeByName("DEFAULT"))
- {
- if(pair.second->getAttributeByName("DEFAULT")->value == "YES")
- altAdaptSet->setRole(Role(Role::Value::Main));
- else
- altAdaptSet->setRole(Role(Role::Value::Alternate));
- }
+ adaptSet->addRepresentation(rep);
+ info.rep = rep;
+ }
- if(pair.second->getAttributeByName("AUTOSELECT"))
- {
- if(pair.second->getAttributeByName("AUTOSELECT")->value == "NO" &&
- !pair.second->getAttributeByName("DEFAULT"))
- altAdaptSet->setRole(Role(Role::Value::Supplementary));
- }
+ 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;
- /* Subtitles unsupported for now */
- const Attribute *typeattr = pair.second->getAttributeByName("TYPE");
- if(typeattr->value == "SUBTITLES")
+ 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)
{
- altAdaptSet->setRole(Role(Role::Value::Subtitle));
+ 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 if(typeattr->value != "AUDIO" && typeattr->value != "VIDEO")
+ }
+ else /* This is an alternative in the group */
+ {
+ BaseAdaptationSet *altAdaptSet = new (std::nothrow) BaseAdaptationSet(period);
+ if(altAdaptSet)
{
- rep->streamFormat = StreamFormat(StreamFormat::Type::Unsupported);
- }
+ fillAdaptsetFromMediainfo(mediainfo.tag, typeattr->value, mediainfo.group, altAdaptSet);
- if(pair.second->getAttributeByName("LANGUAGE"))
- altAdaptSet->setLang(pair.second->getAttributeByName("LANGUAGE")->quotedString());
+ HLSRepresentation *rep = createRepresentation(altAdaptSet, mediainfo.tag);
+ if(!rep)
+ {
+ delete altAdaptSet;
+ continue;
+ }
- if(!altAdaptSet->getRepresentations().empty())
- period->addAdaptationSet(altAdaptSet);
- else
- delete altAdaptSet;
+ 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) */
{
=====================================
modules/demux/hls/playlist/Parser.hpp
=====================================
@@ -65,6 +65,10 @@ namespace hls
HLSRepresentation * createRepresentation(BaseAdaptationSet *, const AttributesTag *);
void createAndFillRepresentation(vlc_object_t *, BaseAdaptationSet *,
const AttributesTag *, const std::list<Tag *>&);
+ void fillRepresentationFromMediainfo(const AttributesTag *, const std::string &,
+ HLSRepresentation *);
+ void fillAdaptsetFromMediainfo(const AttributesTag *, const std::string &,
+ const std::string &, BaseAdaptationSet *);
void parseSegments(vlc_object_t *, HLSRepresentation *, const std::list<Tag *>&);
std::list<Tag *> parseEntries(stream_t *);
adaptive::SharedResources *resources;
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/5e58b6a9fa37d2fa11384cce6714c52811281146...ee3213605d176c0a884b1d93dff7fc48b8848400
--
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/5e58b6a9fa37d2fa11384cce6714c52811281146...ee3213605d176c0a884b1d93dff7fc48b8848400
You're receiving this email because of your account on code.videolan.org.
VideoLAN code repository instance
More information about the vlc-commits
mailing list