Skip to content

Commit

Permalink
[DASH] More rework to support manifest live updates
Browse files Browse the repository at this point in the history
  • Loading branch information
peak3d committed Aug 22, 2017
1 parent 701f590 commit fd51d29
Show file tree
Hide file tree
Showing 10 changed files with 145 additions and 32 deletions.
6 changes: 5 additions & 1 deletion src/common/AdaptiveStream.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -491,7 +491,11 @@ bool AdaptiveStream::select_stream(bool force, bool justInit, unsigned int repId
}

/* lets download the initialization */
if ((current_seg_ = current_rep_->get_initialization()) && !download_segment())
current_seg_ = current_rep_->get_initialization();
if (!current_seg_ && current_rep_->flags_ & AdaptiveTree::Representation::INITIALIZATION_PREFIXED)
current_seg_ = current_rep_->get_segment(segid);

if (current_seg_ && !download_segment())
return false;

current_seg_ = current_rep_->get_segment(segid);
Expand Down
46 changes: 45 additions & 1 deletion src/common/AdaptiveTree.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include <string.h>
#include <algorithm>
#include <stdlib.h>
#include "../log.h"

namespace adaptive
{
Expand All @@ -37,6 +38,7 @@ namespace adaptive

AdaptiveTree::AdaptiveTree()
: current_period_(0)
, update_parameter_pos_(std::string::npos)
, parser_(0)
, currentNode_(0)
, segcount_(0)
Expand Down Expand Up @@ -66,6 +68,8 @@ namespace adaptive
delete[] bs->url;
for (std::vector<Segment>::iterator bs((*br)->newSegments_.data.begin()), es((*br)->newSegments_.data.end()); bs != es; ++bs)
delete[] bs->url;
if((*br)->flags_ & Representation::INITIALIZATION)
delete[] (*br)->initialization_.url;
}
}

Expand Down Expand Up @@ -99,7 +103,7 @@ namespace adaptive

void AdaptiveTree::SetFragmentDuration(const AdaptationSet* adp, const Representation* rep, size_t pos, uint64_t timestamp, uint32_t fragmentDuration, uint32_t movie_timescale)
{
if (!has_timeshift_buffer_)
if (!has_timeshift_buffer_ || (rep->flags_ & AdaptiveTree::Representation::URLSEGMENTS) != 0)
return;

//Get a modifiable adaptationset
Expand Down Expand Up @@ -171,6 +175,46 @@ namespace adaptive
return 0;
}

bool AdaptiveTree::PreparePaths(const std::string &url)
{
size_t paramPos = url.find('?');
base_url_ = (paramPos == std::string::npos) ? url : url.substr(0, paramPos);

paramPos = base_url_.find_last_of('/', base_url_.length());
if (paramPos == std::string::npos)
{
Log(LOGLEVEL_ERROR, "Invalid mpdURL: / expected (%s)", manifest_url_.c_str());
return false;
}
base_url_.resize(paramPos + 1);
base_domain_ = base_url_;

paramPos = base_url_.find_first_of('/', 8);
if (paramPos != std::string::npos)
base_domain_.resize(paramPos);

manifest_url_ = url;

std::string::size_type repPos = manifest_url_.find("$START_NUMBER$");
if (repPos != std::string::npos)
{
while (repPos && manifest_url_[repPos] != '&' && manifest_url_[repPos] != '?')--repPos;
if (repPos)
{
update_parameter_ = manifest_url_.substr(repPos);
manifest_url_.resize(manifest_url_.size() - update_parameter_.size());
update_parameter_pos_ = update_parameter_.find("$START_NUMBER$");
if (manifest_url_.find("?") == std::string::npos)
update_parameter_[0] = '?';
}
else
{
Log(LOGLEVEL_ERROR, "Cannot find update parameter delimiter (%s)", manifest_url_.c_str());
}
}
return true;
}

void AdaptiveTree::SortTree()
{
for (std::vector<Period*>::const_iterator bp(periods_.begin()), ep(periods_.end()); bp != ep; ++bp)
Expand Down
16 changes: 13 additions & 3 deletions src/common/AdaptiveTree.h
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,10 @@ namespace adaptive
basePos = 0;
}

bool empty() const { return data.empty(); };

size_t size() const { return data.size(); };

std::vector<T> data;
};

Expand Down Expand Up @@ -127,7 +131,7 @@ namespace adaptive
{
Representation() :bandwidth_(0), samplingRate_(0), width_(0), height_(0), fpsRate_(0), fpsScale_(1), aspect_(0.0f),
flags_(0), hdcpVersion_(0), indexRangeMin_(0), indexRangeMax_(0), channelCount_(0), nalLengthSize_(0), pssh_set_(0), expired_segments_(0),
containerType_(AdaptiveTree::CONTAINERTYPE_MP4), startNumber_(1), newStartNumber_(~0), duration_(0), timescale_(0) {};
containerType_(AdaptiveTree::CONTAINERTYPE_MP4), startNumber_(1), newStartNumber_(~0), nextPts_(0), duration_(0), timescale_(0) {};
std::string url_;
std::string id;
std::string codecs_;
Expand All @@ -150,6 +154,8 @@ namespace adaptive
static const uint16_t URLSEGMENTS = 128;
static const uint16_t ENABLED = 256;
static const uint16_t HASUPDATESEGMENTS = 512;
static const uint16_t INITIALIZATION_PREFIXED = 1024;


uint16_t flags_;
uint16_t hdcpVersion_;
Expand All @@ -161,6 +167,7 @@ namespace adaptive
ContainerType containerType_;
SegmentTemplate segtpl_;
unsigned int startNumber_, newStartNumber_;
uint64_t nextPts_;
//SegmentList
uint32_t duration_, timescale_;
uint32_t timescale_ext_, timescale_int_;
Expand Down Expand Up @@ -227,6 +234,7 @@ namespace adaptive
std::string language_;
std::string mimeType_;
std::string base_url_;
std::string id;
std::string codecs_;
std::vector<Representation*> repesentations_;
SPINCACHE<uint32_t> segment_durations_;
Expand Down Expand Up @@ -254,7 +262,8 @@ namespace adaptive
}*current_period_;

std::vector<Period*> periods_;
std::string base_url_, base_domain_;
std::string manifest_url_, base_url_, base_domain_, update_parameter_;
std::string::size_type update_parameter_pos_;

/* XML Parsing*/
XML_Parser parser_;
Expand Down Expand Up @@ -308,7 +317,7 @@ namespace adaptive
AdaptiveTree();
virtual ~AdaptiveTree();

virtual bool open(const char *url) = 0;
virtual bool open(const std::string &url) = 0;
virtual bool prepareRepresentation(Representation *rep, bool update = false) { return true; };
virtual void OnDataArrived(Representation *rep, const Segment *seg, const uint8_t *src, uint8_t *dst, size_t dstOffset, size_t dataSize);
virtual void RefreshSegments(Representation *rep, const Segment *seg) {};
Expand All @@ -326,6 +335,7 @@ namespace adaptive
protected:
virtual bool download(const char* url, const std::map<std::string, std::string> &manifestHeaders);
virtual bool write_data(void *buffer, size_t buffer_size) = 0;
bool PreparePaths(const std::string &url);
void SortTree();
private:
std::mutex m_mutex;
Expand Down
20 changes: 3 additions & 17 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1552,22 +1552,6 @@ bool Session::initialize()
}

// Open mpd file
size_t paramPos = mpdFileURL_.find('?');
adaptiveTree_->base_url_ = (paramPos == std::string::npos) ? mpdFileURL_ : mpdFileURL_.substr(0, paramPos);

paramPos = adaptiveTree_->base_url_.find_last_of('/', adaptiveTree_->base_url_.length());
if (paramPos == std::string::npos)
{
kodi::Log(ADDON_LOG_ERROR, "Invalid mpdURL: / expected (%s)", mpdFileURL_.c_str());
return false;
}
adaptiveTree_->base_url_.resize(paramPos + 1);
adaptiveTree_->base_domain_ = adaptiveTree_->base_url_;

paramPos = adaptiveTree_->base_url_.find_first_of('/', 8);
if (paramPos != std::string::npos)
adaptiveTree_->base_domain_.resize(paramPos);

if (!adaptiveTree_->open(mpdFileURL_.c_str()) || adaptiveTree_->empty())
{
kodi::Log(ADDON_LOG_ERROR, "Could not open / parse mpdURL (%s)", mpdFileURL_.c_str());
Expand Down Expand Up @@ -1906,7 +1890,9 @@ AP4_Movie *Session::PrepareStream(STREAM *stream)
if (!adaptiveTree_->prepareRepresentation(const_cast<adaptive::AdaptiveTree::Representation *>(stream->stream_.getRepresentation())))
return nullptr;

if (GetManifestType() == MANIFEST_TYPE_ISM && stream->stream_.getRepresentation()->get_initialization() == nullptr)
if (stream->stream_.getRepresentation()->containerType_ == adaptive::AdaptiveTree::CONTAINERTYPE_MP4
&& (stream->stream_.getRepresentation()->flags_ & adaptive::AdaptiveTree::Representation::INITIALIZATION_PREFIXED) == 0
&& stream->stream_.getRepresentation()->get_initialization() == nullptr)
{
//We'll create a Movie out of the things we got from manifest file
//note: movie will be deleted in destructor of stream->input_file_
Expand Down
69 changes: 66 additions & 3 deletions src/parser/DASHTree.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,7 @@ start(void *data, const char *el, const char **attr)
else if (dash->currentNode_ & DASHTree::MPDNODE_SEGMENTLIST)
{
DASHTree::Segment seg;
seg.pssh_set_ = 0;
if (strcmp(el, "SegmentURL") == 0)
{
for (; *attr;)
Expand Down Expand Up @@ -462,6 +463,7 @@ start(void *data, const char *el, const char **attr)
s.range_begin_ = (t += d);
s.startPTS_ += d;
}
dash->current_representation_->nextPts_ = s.startPTS_;
}
else //Failure
{
Expand Down Expand Up @@ -641,6 +643,7 @@ start(void *data, const char *el, const char **attr)
dash->current_representation_->url_ = dash->current_adaptationset_->base_url_;
dash->current_representation_->timescale_ = dash->current_adaptationset_->timescale_;
dash->current_representation_->duration_ = dash->current_adaptationset_->duration_;
dash->current_representation_->startNumber_ = dash->current_adaptationset_->startNumber_;
dash->current_adaptationset_->repesentations_.push_back(dash->current_representation_);
dash->current_representation_->width_ = dash->adpwidth_;
dash->current_representation_->height_ = dash->adpheight_;
Expand Down Expand Up @@ -799,6 +802,8 @@ start(void *data, const char *el, const char **attr)
: stricmp((const char*)*(attr + 1), "audio") == 0 ? DASHTree::AUDIO
: stricmp((const char*)*(attr + 1), "text") == 0 ? DASHTree::SUBTITLE
: DASHTree::NOTYPE;
else if (strcmp((const char*)*attr, "id") == 0)
dash->current_adaptationset_->id = (const char*)*(attr + 1);
else if (strcmp((const char*)*attr, "lang") == 0)
dash->current_adaptationset_->language_ = ltranslate((const char*)*(attr + 1));
else if (strcmp((const char*)*attr, "mimeType") == 0)
Expand Down Expand Up @@ -849,6 +854,8 @@ start(void *data, const char *el, const char **attr)
dash->current_period_->duration_ = atoi((const char*)*(attr + 1));
else if (strcmp((const char*)*attr, "timescale") == 0)
dash->current_period_->timescale_ = atoi((const char*)*(attr + 1));
else if (strcmp((const char*)*attr, "startNumber") == 0)
dash->current_period_->startNumber_ = atoi((const char*)*(attr + 1));
attr += 2;
}
if (dash->current_period_->timescale_)
Expand Down Expand Up @@ -1093,6 +1100,7 @@ end(void *data, const char *el)
seg.startPTS_ += duration, seg.range_begin_ += duration;
++seg.range_end_;
}
dash->current_representation_->nextPts_ = seg.startPTS_;
return;
}
}
Expand All @@ -1110,6 +1118,9 @@ end(void *data, const char *el)
ReplacePlaceHolders(dash->current_representation_->segtpl_.media, dash->current_representation_->id, dash->current_representation_->bandwidth_);
}
}
if ((dash->current_representation_->flags_ & AdaptiveTree::Representation::INITIALIZATION) == 0 && !dash->current_representation_->segments_.empty())
// we assume that we have a MOOV atom included in each segment (max 100k = youtube)
dash->current_representation_->flags_ |= AdaptiveTree::Representation::INITIALIZATION_PREFIXED;
}
else if (dash->currentNode_ & DASHTree::MPDNODE_SEGMENTDURATIONS)
{
Expand Down Expand Up @@ -1206,6 +1217,7 @@ end(void *data, const char *el)
sb->startPTS_ = spts;
spts += *sdb;
}
(*b)->nextPts_ = spts;
}
}
}
Expand Down Expand Up @@ -1268,24 +1280,28 @@ end(void *data, const char *el)
| DASHTree
+---------------------------------------------------------------------*/

bool DASHTree::open(const char *url)
bool DASHTree::open(const std::string &url)
{
PreparePaths(url);
parser_ = XML_ParserCreate(NULL);
if (!parser_)
return false;

XML_SetUserData(parser_, (void*)this);
XML_SetElementHandler(parser_, start, end);
XML_SetCharacterDataHandler(parser_, text);
currentNode_ = 0;
strXMLText_.clear();

bool ret = download(url, manifest_headers_);
bool ret = download(manifest_url_.c_str(), manifest_headers_);

XML_ParserFree(parser_);
parser_ = 0;

SortTree();

last_update_time_ = std::chrono::steady_clock::now();

return ret;
}

Expand All @@ -1301,3 +1317,50 @@ bool DASHTree::write_data(void *buffer, size_t buffer_size)
}
return true;
}

void DASHTree::RefreshSegments(Representation *rep, const Segment *seg)
{
if (has_timeshift_buffer_ && !update_parameter_.empty())
{
std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now();
if (std::chrono::duration_cast<std::chrono::seconds>(now - last_update_time_).count() > 1)
{
last_update_time_ = now;
unsigned int nextStartNumber(rep->startNumber_ + rep->segments_.size());

char buf[32];
sprintf(buf, "%u", nextStartNumber);
std::string replaced(update_parameter_);
replaced.replace(update_parameter_pos_, 14, buf);

DASHTree updateTree;
if (updateTree.open(manifest_url_ + replaced))
{
std::vector<Period*>::const_iterator bpd(periods_.begin()), epd(periods_.end());
for (std::vector<Period*>::const_iterator bp(updateTree.periods_.begin()), ep(updateTree.periods_.end()); bp != ep && bpd != epd; ++bp, ++bpd)
{
for (std::vector<AdaptationSet*>::const_iterator ba((*bp)->adaptationSets_.begin()), ea((*bp)->adaptationSets_.end()); ba != ea; ++ba)
{
//Locate adaptationset
std::vector<AdaptationSet*>::const_iterator bad((*bpd)->adaptationSets_.begin()), ead((*bpd)->adaptationSets_.end());
for (; bad != ead && (*bad)->id != (*ba)->id; ++bad);
if (bad != ead)
{
for (std::vector<Representation*>::iterator br((*ba)->repesentations_.begin()), er((*ba)->repesentations_.end()); br != er; ++br)
{
//Locate representation
std::vector<Representation*>::const_iterator brd((*bad)->repesentations_.begin()), erd((*bad)->repesentations_.end());
for (; brd != erd && (*brd)->id != (*br)->id; ++brd);
if (brd != erd && !(*br)->segments_.empty())
{
//Here we go -> Insert new segments
uint64_t ptsOffset = (*brd)->nextPts_ - (*br)->segments_[0]->startPTS_;
}
}
}
}
}
}
}
}
}
5 changes: 4 additions & 1 deletion src/parser/DASHTree.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#pragma once

#include "../common/AdaptiveTree.h"
#include <chrono>

namespace adaptive
{
Expand All @@ -27,8 +28,9 @@ namespace adaptive
{
public:
DASHTree();
virtual bool open(const char *url) override;
virtual bool open(const std::string &url) override;
virtual bool write_data(void *buffer, size_t buffer_size) override;
virtual void RefreshSegments(Representation *rep, const Segment *seg) override;

enum
{
Expand All @@ -48,5 +50,6 @@ namespace adaptive
MPDNODE_SEGMENTTIMELINE = 1 << 14
};
uint64_t pts_helper_;
std::chrono::steady_clock::time_point last_update_time_;
};
}
5 changes: 3 additions & 2 deletions src/parser/HLSTree.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,9 +82,10 @@ void HLSTree::ClearStream()
m_stream.clear();
}

bool HLSTree::open(const char *url)
bool HLSTree::open(const std::string &url)
{
if (download(url, manifest_headers_))
PreparePaths(url);
if (download(manifest_url_.c_str(), manifest_headers_))
{
#if FILEDEBUG
FILE *f = fopen("inputstream_adaptive_master.m3u8", "w");
Expand Down
2 changes: 1 addition & 1 deletion src/parser/HLSTree.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ namespace adaptive
HLSTree(AESDecrypter *decrypter) : AdaptiveTree(), m_decrypter(decrypter) {};
virtual ~HLSTree();

virtual bool open(const char *url) override;
virtual bool open(const std::string &url) override;
virtual bool prepareRepresentation(Representation *rep, bool update = false) override;
virtual bool write_data(void *buffer, size_t buffer_size) override;
virtual void OnDataArrived(Representation *rep, const Segment *seg, const uint8_t *src, uint8_t *dst, size_t dstOffset, size_t dataSize) override;
Expand Down
Loading

0 comments on commit fd51d29

Please sign in to comment.