Skip to content

Commit

Permalink
Merge pull request #1562 from CastagnaIT/pr_cleanup_fix
Browse files Browse the repository at this point in the history
AdaptiveTree methods cleanups and fix period change for HLS case
  • Loading branch information
CastagnaIT authored May 28, 2024
2 parents 2bc8dad + 774d8f8 commit 206bc70
Show file tree
Hide file tree
Showing 15 changed files with 199 additions and 165 deletions.
39 changes: 25 additions & 14 deletions src/Session.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -573,16 +573,14 @@ bool CSession::InitializePeriod(bool isSessionOpened /* = false */)
{
bool isPsshChanged{true};
bool isReusePssh{true};
bool isPeriodChange = m_adaptiveTree->m_nextPeriod;

if (m_adaptiveTree->m_nextPeriod)
if (m_adaptiveTree->IsChangingPeriod())
{
isPsshChanged =
!(m_adaptiveTree->m_currentPeriod->GetPSSHSets() == m_adaptiveTree->m_nextPeriod->GetPSSHSets());
isReusePssh = !isPsshChanged && m_adaptiveTree->m_nextPeriod->GetEncryptionState() ==
EncryptionState::ENCRYPTED_DRM;
m_adaptiveTree->m_currentPeriod = m_adaptiveTree->m_nextPeriod;
m_adaptiveTree->m_nextPeriod = nullptr;
}

m_chapterStartTime = GetChapterStartTime();
Expand Down Expand Up @@ -660,7 +658,7 @@ bool CSession::InitializePeriod(bool isSessionOpened /* = false */)
CRepresentation* currentRepr = adp->GetRepresentations()[i].get();
bool isDefaultRepr{currentRepr == defaultRepr};

AddStream(adp, currentRepr, isDefaultRepr, uniqueId, audioLanguageOrig, isPeriodChange);
AddStream(adp, currentRepr, isDefaultRepr, uniqueId, audioLanguageOrig);
}
}
else
Expand All @@ -670,7 +668,7 @@ bool CSession::InitializePeriod(bool isSessionOpened /* = false */)
uint32_t uniqueId{adpIndex};
uniqueId |= reprIndex << 16;

AddStream(adp, defaultRepr, true, uniqueId, audioLanguageOrig, isPeriodChange);
AddStream(adp, defaultRepr, true, uniqueId, audioLanguageOrig);
}
}

Expand All @@ -681,8 +679,7 @@ void CSession::AddStream(PLAYLIST::CAdaptationSet* adp,
PLAYLIST::CRepresentation* initialRepr,
bool isDefaultRepr,
uint32_t uniqueId,
std::string_view audioLanguageOrig,
const bool isPeriodChange)
std::string_view audioLanguageOrig)
{
m_streams.push_back(std::make_unique<CStream>(m_adaptiveTree, adp, initialRepr));

Expand Down Expand Up @@ -735,7 +732,6 @@ void CSession::AddStream(PLAYLIST::CAdaptationSet* adp,
stream.m_info.ClearExtraData();
stream.m_info.SetFeatures(0);

stream.m_adStream.SetStartEvent(isPeriodChange ? EVENT_TYPE::PERIOD_CHANGE : EVENT_TYPE::STREAM_START);
stream.m_adStream.set_observer(dynamic_cast<adaptive::AdaptiveStreamObserver*>(this));

UpdateStream(stream);
Expand Down Expand Up @@ -933,18 +929,19 @@ void CSession::UpdateStream(CStream& stream)

void CSession::PrepareStream(CStream* stream)
{
if (m_adaptiveTree->GetTreeType() != adaptive::TreeType::HLS)
if (!m_adaptiveTree->IsReqPrepareStream())
return;

CRepresentation* repr = stream->m_adStream.getRepresentation();
const EVENT_TYPE startEvent = stream->m_adStream.GetStartEvent();

// Download the manifest only at first start of the stream
if (startEvent == EVENT_TYPE::STREAM_START)
// Prepare the representation when the period change usually its not needed,
// because the timeline is always already updated
if ((!m_adaptiveTree->IsChangingPeriod() || !repr->HasSegmentTimeline()) &&
(startEvent == EVENT_TYPE::STREAM_START || startEvent == EVENT_TYPE::STREAM_ENABLE))
{
m_adaptiveTree->PrepareRepresentation(stream->m_adStream.getPeriod(),
stream->m_adStream.getAdaptationSet(), repr,
SEGMENT_NO_NUMBER);
stream->m_adStream.getAdaptationSet(), repr);
}

if (startEvent != EVENT_TYPE::REP_CHANGE &&
Expand Down Expand Up @@ -1252,6 +1249,20 @@ bool CSession::SeekTime(double seekTime, unsigned int streamId, bool preceeding)
return ret;
}

void CSession::OnDemuxRead()
{
if (m_adaptiveTree->IsChangingPeriod() && m_adaptiveTree->IsChangingPeriodDone())
{
m_adaptiveTree->m_nextPeriod = nullptr;

if (GetChapterSeekTime() > 0)
{
SeekTime(GetChapterSeekTime());
ResetChapterSeekTime();
}
}
}

void CSession::OnSegmentChanged(adaptive::AdaptiveStream* adStream)
{
for (auto& stream : m_streams)
Expand Down Expand Up @@ -1403,7 +1414,7 @@ int CSession::GetPeriodId() const

bool CSession::SeekChapter(int ch)
{
if (m_adaptiveTree->m_nextPeriod)
if (m_adaptiveTree->IsChangingPeriod())
return true;

--ch;
Expand Down
8 changes: 6 additions & 2 deletions src/Session.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,7 @@ class ATTR_DLL_LOCAL CSession : public adaptive::AdaptiveStreamObserver
PLAYLIST::CRepresentation* repr,
bool isDefaultRepr,
uint32_t uniqueId,
std::string_view audioLanguageOrig,
const bool isPeriodChange);
std::string_view audioLanguageOrig);

/*! \brief Update stream's InputstreamInfo
* \param stream The stream to update
Expand Down Expand Up @@ -293,6 +292,11 @@ class ATTR_DLL_LOCAL CSession : public adaptive::AdaptiveStreamObserver
*/
void ResetChapterSeekTime() { m_chapterSeekTime = 0; };

/*!
* \brief Callback from DemuxRead method of InputStream interface
*/
void OnDemuxRead();

//Observer Section

/*! \brief Sets the current pts offset.
Expand Down
55 changes: 20 additions & 35 deletions src/common/AdaptiveStream.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,6 @@ AdaptiveStream::AdaptiveStream(AdaptiveTree* tree,
auto& kodiProps = CSrvBroker::GetKodiProps();
m_streamParams = kodiProps.GetStreamParams();
m_streamHeaders = kodiProps.GetStreamHeaders();
play_timeshift_buffer_ = kodiProps.IsPlayTimeshift();

current_rep_->current_segment_ = nullptr;

Expand Down Expand Up @@ -678,7 +677,8 @@ bool AdaptiveStream::start_stream(const uint64_t startPts)

if (!current_rep_->current_segment_)
{
if (m_startEvent == EVENT_TYPE::STREAM_START && m_tree->IsLive() && !play_timeshift_buffer_ &&
if (m_startEvent == EVENT_TYPE::STREAM_START && m_tree->IsLive() &&
!m_tree->IsChangingPeriod() && !CSrvBroker::GetKodiProps().IsPlayTimeshift() &&
!current_rep_->SegmentTimeline().IsEmpty())
{
size_t segPos = current_rep_->SegmentTimeline().GetSize() - 1;
Expand Down Expand Up @@ -789,29 +789,6 @@ bool AdaptiveStream::start_stream(const uint64_t startPts)
return false;
}

void AdaptiveStream::ReplacePlaceholder(std::string& url, const std::string placeholder, uint64_t value)
{
std::string::size_type lenReplace(placeholder.length());
std::string::size_type np(url.find(placeholder));
char rangebuf[128];

if (np == std::string::npos)
return;

np += lenReplace;

std::string::size_type npe(url.find('$', np));

char fmt[16];
if (np == npe)
strcpy(fmt, "%" PRIu64);
else
strcpy(fmt, url.substr(np, npe - np).c_str());

sprintf(rangebuf, fmt, value);
url.replace(np - lenReplace, npe - np + lenReplace + 1, rangebuf);
}

bool AdaptiveStream::ensureSegment()
{
// NOTE: Some demuxers may call ensureSegment more times to try make more attempts when it return false.
Expand All @@ -832,10 +809,15 @@ bool AdaptiveStream::ensureSegment()
// lock live segment updates
std::lock_guard<adaptive::AdaptiveTree::TreeUpdateThread> lckUpdTree(m_tree->GetTreeUpdMutex());

if (m_tree->HasManifestUpdatesSegs() && SecondsSinceUpdate() > 1)
if (m_tree->HasManifestUpdatesSegs())
{
m_tree->RefreshSegments(current_period_, current_adp_, current_rep_);
lastUpdated_ = std::chrono::system_clock::now();
// Limit requests with an interval of at least 1 second,
// to avoid overloading servers with too requests
if (SecondsSinceUpdate() > 1)
{
m_tree->OnRequestSegments(current_period_, current_adp_, current_rep_);
lastUpdated_ = std::chrono::system_clock::now();
}
}

if (m_fixateInitialization)
Expand Down Expand Up @@ -947,12 +929,12 @@ bool AdaptiveStream::ensureSegment()
else
newRep = m_tree->GetRepChooser()->GetNextRepresentation(current_adp_, prevRep);

//! @todo: There is the possibility that stream quality switching happen frequently in very short time,
//! so if OnStreamChange is used on a parser, it could overload servers of manifest requests
//! a minimum interval should be considered to avoid too switches in a too short period of time
if (newRep != prevRep) // Stream quality changed
{
// On manifests type like HLS we need also to get update segments
// because need to be downloaded/parsed from different child manifest files
m_tree->PrepareRepresentation(current_period_, current_adp_, newRep,
current_rep_->getCurrentSegmentNumber());
m_tree->OnStreamChange(current_period_, current_adp_, current_rep_, newRep);

// If the representation has been changed, segments may have to be generated (DASH)
if (newRep->SegmentTimeline().IsEmpty())
Expand Down Expand Up @@ -1149,9 +1131,12 @@ uint64_t AdaptiveStream::getMaxTimeMs()

void adaptive::AdaptiveStream::Disable()
{
// Prepare the future event to re-enable the stream, but preserve following events
if (m_startEvent != EVENT_TYPE::REP_CHANGE && m_startEvent != EVENT_TYPE::PERIOD_CHANGE)
m_startEvent = EVENT_TYPE::STREAM_ENABLE;
// Preserve following events
if (m_startEvent == EVENT_TYPE::REP_CHANGE)
return;

// Prepare it for the future event
m_startEvent = EVENT_TYPE::STREAM_ENABLE;
}

void AdaptiveStream::ResetCurrentSegment(const PLAYLIST::CSegment* newSegment)
Expand Down
5 changes: 1 addition & 4 deletions src/common/AdaptiveStream.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ enum class EVENT_TYPE
NONE,
STREAM_START, // First start of the stream
STREAM_ENABLE, // Has been re-enabled the disabled stream
PERIOD_CHANGE, // Has been changed period
REP_CHANGE // Has been changed representation (stream quality)
};

Expand Down Expand Up @@ -73,7 +72,6 @@ enum class EVENT_TYPE

void Disable();

void SetStartEvent(const EVENT_TYPE eventType) { m_startEvent = eventType; }
EVENT_TYPE GetStartEvent() const { return m_startEvent; }

/*!
Expand Down Expand Up @@ -226,7 +224,7 @@ enum class EVENT_TYPE
void worker();

int SecondsSinceUpdate() const;
static void ReplacePlaceholder(std::string& url, const std::string placeholder, uint64_t value);

bool GenerateSidxSegments(PLAYLIST::CRepresentation* rep);

struct THREADDATA
Expand Down Expand Up @@ -290,7 +288,6 @@ enum class EVENT_TYPE
std::atomic<bool> worker_processing_;
bool m_fixateInitialization;
uint64_t m_segmentFileOffset;
bool play_timeshift_buffer_;

// Defines the event to start the stream, the status will be resetted by start stream method.
EVENT_TYPE m_startEvent{EVENT_TYPE::STREAM_START};
Expand Down
5 changes: 4 additions & 1 deletion src/common/AdaptiveTree.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ namespace adaptive
m_supportedKeySystem = left.m_supportedKeySystem;
m_pathSaveManifest = left.m_pathSaveManifest;
stream_start_ = left.stream_start_;

m_isTTMLTimeRelative = left.m_isTTMLTimeRelative;
m_isReqPrepareStream = left.m_isReqPrepareStream;
}

void AdaptiveTree::Configure(CHOOSER::IRepresentationChooser* reprChooser,
Expand Down Expand Up @@ -288,7 +291,7 @@ namespace adaptive
if (m_resetInterval)
m_tree->m_updateInterval = PLAYLIST::NO_VALUE;

m_tree->RefreshLiveSegments();
m_tree->OnUpdateSegments();
}
}
}
Expand Down
60 changes: 53 additions & 7 deletions src/common/AdaptiveTree.h
Original file line number Diff line number Diff line change
Expand Up @@ -125,8 +125,7 @@ class ATTR_DLL_LOCAL AdaptiveTree
*/
virtual bool PrepareRepresentation(PLAYLIST::CPeriod* period,
PLAYLIST::CAdaptationSet* adp,
PLAYLIST::CRepresentation* rep,
uint64_t currentSegNumber)
PLAYLIST::CRepresentation* rep)
{
return false;
}
Expand All @@ -146,9 +145,30 @@ class ATTR_DLL_LOCAL AdaptiveTree
size_t segBufferSize,
bool isLastChunk);

virtual void RefreshSegments(PLAYLIST::CPeriod* period,
PLAYLIST::CAdaptationSet* adp,
PLAYLIST::CRepresentation* rep)
/*!
* \brief Callback that request new segments each time the demuxer reads, for the specified representation.
* Intended for live streaming that does not have a defined update time interval.
* \param period Current period
* \param adpSet Current adaptation set
* \param repr The representation where update the timeline
*/
virtual void OnRequestSegments(PLAYLIST::CPeriod* period,
PLAYLIST::CAdaptationSet* adp,
PLAYLIST::CRepresentation* rep)
{
}

/*!
* \brief Callback done when the stream (representation) quality has been changed.
* \param period Current period
* \param adpSet Current adaptation set
* \param previousRep The previous representation
* \param currentRep The new current representation
*/
virtual void OnStreamChange(PLAYLIST::CPeriod* period,
PLAYLIST::CAdaptationSet* adp,
PLAYLIST::CRepresentation* previousRep,
PLAYLIST::CRepresentation* currentRep)
{
}

Expand Down Expand Up @@ -205,6 +225,18 @@ class ATTR_DLL_LOCAL AdaptiveTree
: nullptr;
}

/*!
* \brief Checks if a period change is in progress (m_nextPeriod is set).
* \return True the period will be changed, otherwise false.
*/
bool IsChangingPeriod() const { return m_nextPeriod; }

/*!
* \brief Check if the period change has been made.
* \return True the period is changed, otherwise false.
*/
bool IsChangingPeriodDone() const { return m_nextPeriod == m_currentPeriod; }

/*!
* \brief Check for live streaming content (timeshift buffer)
* \return True for live streaming content, otherwise false for VOD content
Expand Down Expand Up @@ -251,7 +283,7 @@ class ATTR_DLL_LOCAL AdaptiveTree
// \brief Reset start time (make exit the condition variable m_cvUpdInterval and re-start the timeout)
void ResetStartTime() { m_cvUpdInterval.notify_all(); }

// \brief At next update reset the interval value to NO_VALUE, before make RefreshLiveSegments callback
// \brief At next update reset the interval value to NO_VALUE, before make OnUpdateSegments callback
void ResetInterval() { m_resetInterval = true; }

// \brief As "std::mutex" lock, but put in pause the manifest updates (support std::lock_guard).
Expand Down Expand Up @@ -307,6 +339,14 @@ class ATTR_DLL_LOCAL AdaptiveTree
*/
bool IsTTMLTimeRelative() const { return m_isTTMLTimeRelative; }

/*!
* \brief Specifies if the manifest parser require to prepare the stream representation.
* Usually this is needed for protocols that use separate manifests
* for each stream such as HLS.
* \return True if prepare the stream is required, otherwise false.
*/
bool IsReqPrepareStream() const { return m_isReqPrepareStream; }

/*!
* \brief Check if specified segment is the last of current period.
* \param segPeriod The period relative to the segment
Expand Down Expand Up @@ -334,7 +374,12 @@ class ATTR_DLL_LOCAL AdaptiveTree
// Live segment update section
bool m_isLive{false};
virtual void StartUpdateThread();
virtual void RefreshLiveSegments() { lastUpdated_ = std::chrono::system_clock::now(); }

/*!
* \brief Callback that request new segments, used with TreeUpdateThread worker.
* Intended for live streaming that does have a defined update time interval.
*/
virtual void OnUpdateSegments() { lastUpdated_ = std::chrono::system_clock::now(); }

// Manifest update interval in ms,
// Non-zero value: refresh interval starting from the moment mpd download was initiated
Expand All @@ -355,6 +400,7 @@ class ATTR_DLL_LOCAL AdaptiveTree
std::string m_licenseUrl;

bool m_isTTMLTimeRelative{false};
bool m_isReqPrepareStream{false};
};

} // namespace adaptive
Loading

0 comments on commit 206bc70

Please sign in to comment.