Skip to content

Commit

Permalink
Merge pull request #1325 from CastagnaIT/ss
Browse files Browse the repository at this point in the history
[SmoothTree] Regression fixes and better logging
  • Loading branch information
glennguy authored Jul 17, 2023
2 parents 3ab3a3c + 6871e86 commit 5f64aa9
Show file tree
Hide file tree
Showing 27 changed files with 672 additions and 195 deletions.
34 changes: 18 additions & 16 deletions src/Session.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1255,7 +1255,12 @@ bool CSession::GetNextSample(ISampleReader*& sampleReader)
}
else if (res)
{
CheckFragmentDuration(*res);
if (res->m_hasSegmentChanged)
{
OnSegmentChangedRead(res);
res->m_hasSegmentChanged = false;
}

ISampleReader* sr{res->GetReader()};

if (sr->PTS() != STREAM_NOPTS_VALUE)
Expand Down Expand Up @@ -1432,25 +1437,22 @@ void CSession::OnStreamChange(adaptive::AdaptiveStream* adStream)
}
}

void CSession::CheckFragmentDuration(CStream& stream)
void CSession::OnSegmentChangedRead(CStream* stream)
{
uint64_t nextTs;
uint64_t nextDur;
ISampleReader* streamReader{stream.GetReader()};
if (!streamReader)
if (m_adaptiveTree->IsLive())
{
LOG::LogF(LOGERROR, "Cannot get the stream sample reader");
return;
}
ISampleReader* sr = stream->GetReader();
uint64_t duration;

if (stream.m_hasSegmentChanged && streamReader->GetNextFragmentInfo(nextTs, nextDur))
{
m_adaptiveTree->SetFragmentDuration(
stream.m_adStream.getPeriod(), stream.m_adStream.getAdaptationSet(),
stream.m_adStream.getRepresentation(), stream.m_adStream.getSegmentPos(), nextTs,
static_cast<uint32_t>(nextDur), streamReader->GetTimeScale());
if (sr->GetFragmentInfo(duration))
{
adaptive::AdaptiveStream& adStream = stream->m_adStream;

m_adaptiveTree->InsertLiveSegment(adStream.getPeriod(), adStream.getAdaptationSet(),
adStream.getRepresentation(), adStream.getSegmentPos(),
0, duration, sr->GetTimeScale());
}
}
stream.m_hasSegmentChanged = false;
}

const AP4_UI08* CSession::GetDefaultKeyId(const uint16_t index) const
Expand Down
9 changes: 5 additions & 4 deletions src/Session.h
Original file line number Diff line number Diff line change
Expand Up @@ -334,11 +334,12 @@ class ATTR_DLL_LOCAL CSession : public adaptive::AdaptiveStreamObserver
void OnStreamChange(adaptive::AdaptiveStream* adStream) override;

protected:
/*! \brief If available, read the duration and timestamp of the next fragment and
* set the related members
* \param adStream [OUT] The adaptive stream to check
/*!
* \brief Event raised when the current segment is changed and
* the data has already been read by the sample reader.
* \param stream The stream for which segment has changed.
*/
void CheckFragmentDuration(CStream& stream);
void OnSegmentChangedRead(CStream* stream);

/*! \brief Check for and load decrypter module matching the supplied key system
* \param key_system [OUT] Will be assigned to if a decrypter is found matching
Expand Down
6 changes: 6 additions & 0 deletions src/Stream.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,9 @@ void CStream::Reset()
m_mainId = 0;
}
}

void SESSION::CStream::SetReader(std::unique_ptr<ISampleReader> reader)
{
m_streamReader = std::move(reader);
m_streamReader->SetObserver(&m_adStream);
}
2 changes: 1 addition & 1 deletion src/Stream.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ class ATTR_DLL_LOCAL CStream
* \brief Set the stream sample reader
* \param reader The reader
*/
void SetReader(std::unique_ptr<ISampleReader> reader) { m_streamReader = std::move(reader); }
void SetReader(std::unique_ptr<ISampleReader> reader);

/*!
* \brief Get the stream file handler pointer
Expand Down
6 changes: 6 additions & 0 deletions src/common/AdaptiveStream.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -434,6 +434,12 @@ int AdaptiveStream::SecondsSinceUpdate() const
.count());
}

void AdaptiveStream::OnTFRFatom(uint64_t ts, uint64_t duration, uint32_t mediaTimescale)
{
tree_.InsertLiveSegment(current_period_, current_adp_, current_rep_, getSegmentPos(), ts,
duration, mediaTimescale);
}

bool AdaptiveStream::parseIndexRange(PLAYLIST::CRepresentation* rep,
const std::vector<uint8_t>& buffer)
{
Expand Down
6 changes: 5 additions & 1 deletion src/common/AdaptiveStream.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@

#include "AdaptiveTree.h"

#include "../samplereader/SampleReader.h"

#include <atomic>
#include <condition_variable>
#include <map>
Expand All @@ -30,7 +32,7 @@ class AdaptiveStream;
virtual void OnStreamChange(AdaptiveStream *stream) = 0;
};

class ATTR_DLL_LOCAL AdaptiveStream
class ATTR_DLL_LOCAL AdaptiveStream : public SampleReaderObserver
{
public:
AdaptiveStream(AdaptiveTree& tree,
Expand Down Expand Up @@ -88,6 +90,8 @@ class AdaptiveStream;
void SetSegmentFileOffset(uint64_t offset) { m_segmentFileOffset = offset; };
bool StreamChanged() { return stream_changed_; }

void OnTFRFatom(uint64_t ts, uint64_t duration, uint32_t mediaTimescale) override;

protected:
virtual bool parseIndexRange(PLAYLIST::CRepresentation* rep,
const std::vector<uint8_t>& buffer);
Expand Down
75 changes: 0 additions & 75 deletions src/common/AdaptiveTree.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -96,81 +96,6 @@ namespace adaptive
repr->current_segment_ = nullptr;
}

void AdaptiveTree::SetFragmentDuration(PLAYLIST::CPeriod* period,
PLAYLIST::CAdaptationSet* adpSet,
PLAYLIST::CRepresentation* repr,
size_t pos,
uint64_t timestamp,
uint32_t fragmentDuration,
uint32_t movie_timescale)
{
if (!m_isLive || HasManifestUpdatesSegs())
return;

// Check if its the last frame we watch
if (!adpSet->SegmentTimelineDuration().IsEmpty())
{
if (pos == adpSet->SegmentTimelineDuration().GetSize() - 1)
{
adpSet->SegmentTimelineDuration().Insert(
static_cast<std::uint32_t>(static_cast<std::uint64_t>(fragmentDuration) *
period->GetTimescale() / movie_timescale));
}
else
{
repr->expired_segments_++;
return;
}
}
else if (pos != repr->SegmentTimeline().GetSize() - 1)
return;

// Add new segment
// This may happen for example when a DASH live manifest
// has very long duration validity (set by minimumUpdatePeriod) and segments
// dont cover the entire duration until minimumUpdatePeriod interval time
// in this new segments must be added until the future manifest update
CSegment* segment = repr->SegmentTimeline().Get(pos);

if (!segment)
{
LOG::LogF(LOGERROR, "Segment at position %zu not found from representation id: %s", pos,
repr->GetId().data());
return;
}

if (segment->HasByteRange())
return;

CSegment segCopy = *segment;

if (!timestamp)
{
LOG::LogF(LOGDEBUG, "Scale fragment duration: fdur:%u, rep-scale:%u, mov-scale:%u",
fragmentDuration, repr->GetTimescale(), movie_timescale);
fragmentDuration = static_cast<std::uint32_t>(
(static_cast<std::uint64_t>(fragmentDuration) * repr->GetTimescale()) / movie_timescale);
}
else
{
LOG::LogF(LOGDEBUG, "Fragment duration from timestamp: ts:%llu, base:%llu, s-pts:%llu",
timestamp, base_time_, segCopy.startPTS_);
fragmentDuration = static_cast<uint32_t>(timestamp - base_time_ - segCopy.startPTS_);
}

segCopy.startPTS_ += fragmentDuration;
segCopy.m_time += fragmentDuration;
segCopy.m_number++;

LOG::LogF(LOGDEBUG, "Insert live segment: pts: %llu number: %llu", segCopy.startPTS_,
segCopy.m_number);

for (auto& repr : adpSet->GetRepresentations())
{
repr->SegmentTimeline().Insert(segCopy);
}
}

void AdaptiveTree::OnDataArrived(uint64_t segNum,
uint16_t psshSet,
uint8_t iv[16],
Expand Down
29 changes: 21 additions & 8 deletions src/common/AdaptiveTree.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,6 @@ class ATTR_DLL_LOCAL AdaptiveTree
uint64_t m_totalTimeSecs{0}; // Total playing time in seconds
uint64_t stream_start_{0};
uint64_t available_time_{0};
uint64_t base_time_{0}; // SmoothTree only, the lower start PTS time between all StreamIndex tags
uint64_t m_liveDelay{0}; // Apply a delay in seconds from the live edge

std::string m_supportedKeySystem;
Expand Down Expand Up @@ -133,13 +132,27 @@ class ATTR_DLL_LOCAL AdaptiveTree

void FreeSegments(PLAYLIST::CPeriod* period, PLAYLIST::CRepresentation* repr);

void SetFragmentDuration(PLAYLIST::CPeriod* period,
PLAYLIST::CAdaptationSet* adpSet,
PLAYLIST::CRepresentation* repr,
size_t pos,
uint64_t timestamp,
uint32_t fragmentDuration,
uint32_t movie_timescale);
/*!
* \brief Some adaptive streaming protocols allow the client to download the live playlist once and
* build future segments based on metadata contained in the fragments e.g. to avoid repeated
* manifest downloads or to cover the duration of a period not fully covered by the provided timeline.
* \param period Current period
* \param adpSet Current adaptation set
* \param repr Current representation
* \param pos Current segment position
* \param timestamp Fragment start timestamp
* \param fragmentDuration Fragment duration
* \param movieTimescale Fragment movie timescale
*/
virtual void InsertLiveSegment(PLAYLIST::CPeriod* period,
PLAYLIST::CAdaptationSet* adpSet,
PLAYLIST::CRepresentation* repr,
size_t pos,
uint64_t timestamp,
uint64_t fragmentDuration,
uint32_t movieTimescale)
{
}

// Insert a PSSHSet to the specified Period and return the position
uint16_t InsertPsshSet(PLAYLIST::StreamType streamType,
Expand Down
90 changes: 67 additions & 23 deletions src/common/SegTemplate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,14 @@
#include "SegTemplate.h"
#include "Segment.h"
#include "../utils/log.h"
#include "../utils/StringUtils.h"

#include "kodi/tools/StringUtils.h"

#include <cstdio> // sprintf

using namespace PLAYLIST;
using namespace UTILS;
using namespace kodi::tools;

PLAYLIST::CSegmentTemplate::CSegmentTemplate(CSegmentTemplate* parent /* = nullptr */)
Expand Down Expand Up @@ -90,43 +92,83 @@ CSegment PLAYLIST::CSegmentTemplate::MakeInitSegment()
return seg;
}

std::string PLAYLIST::CSegmentTemplate::FormatUrl(const std::string url,
std::string PLAYLIST::CSegmentTemplate::FormatUrl(std::string_view url,
const std::string id,
const uint32_t bandwidth,
const uint64_t number,
const uint64_t time)
{
std::vector<std::string> chunks = StringUtils::Split(url, '$');
size_t curPos{0};
std::string ret;

if (chunks.size() > 1)
do
{
for (size_t i = 0; i < chunks.size(); i++)
size_t chPos = url.find('$', curPos);
if (chPos == std::string::npos)
{
if (chunks.at(i) == "RepresentationID")
chunks.at(i) = id;
else if (chunks.at(i).find("Bandwidth") == 0)
FormatIdentifier(chunks.at(i), static_cast<uint64_t>(bandwidth));
else if (chunks.at(i).find("Number") == 0)
FormatIdentifier(chunks.at(i), number);
else if (chunks.at(i).find("Time") == 0)
FormatIdentifier(chunks.at(i), time);
// No other identifiers to substitute
ret += url.substr(curPos);
curPos = url.size();
break;
}

std::string replacedUrl = "";
for (size_t i = 0; i < chunks.size(); i++)
ret += url.substr(curPos, chPos - curPos);

size_t nextChPos = url.find('$', chPos + 1);

if (nextChPos == std::string::npos)
nextChPos = url.size();

std::string_view identifier = url.substr(chPos, nextChPos - chPos + 1);

if (identifier == "$$") // Escape sequence
{
replacedUrl += chunks.at(i);
ret += "$";
curPos = nextChPos + 1;
}
return replacedUrl;
}
else if (identifier == "$RepresentationID$")
{
ret += id;
curPos = nextChPos + 1;
}
else if (STRING::StartsWith(identifier, "$Number"))
{
ret += FormatIdentifier(identifier, number);
curPos = nextChPos + 1;
}
else if (STRING::StartsWith(identifier, "$Time"))
{
ret += FormatIdentifier(identifier, time);
curPos = nextChPos + 1;
}
else if (STRING::StartsWith(identifier, "$Bandwidth"))
{
ret += FormatIdentifier(identifier, static_cast<uint64_t>(bandwidth));
curPos = nextChPos + 1;
}
else // Unknow indentifier, or $ char that isnt part of an identifier
{
if (nextChPos != url.size())
identifier.remove_suffix(1);
ret += identifier;
curPos = nextChPos;
}
} while (curPos < url.size());

return ret;
}

std::string PLAYLIST::CSegmentTemplate::FormatIdentifier(std::string_view identifier,
const uint64_t value)
{
if (identifier.back() == '$')
identifier.remove_suffix(1);
else
{
return url;
LOG::LogF(LOGWARNING, "Cannot format template identifier because malformed");
return std::string{identifier};
}
}

void PLAYLIST::CSegmentTemplate::FormatIdentifier(std::string& identifier, const uint64_t value)
{
size_t formatTagIndex = identifier.find("%0");
std::string formatTag = "%01d"; // default format tag

Expand All @@ -143,7 +185,7 @@ void PLAYLIST::CSegmentTemplate::FormatIdentifier(std::string& identifier, const
case 'o':
break; // supported conversions as dash.js
default:
return; // leave as is
return std::string{identifier}; // leave as is
}
}
// sprintf expect the right length of data type
Expand All @@ -155,8 +197,10 @@ void PLAYLIST::CSegmentTemplate::FormatIdentifier(std::string& identifier, const

char substitution[128];
if (std::sprintf(substitution, formatTag.c_str(), value) > 0)
identifier = substitution;
return substitution;
else
LOG::LogF(LOGERROR, "Cannot convert value \"%llu\" with \"%s\" format tag", value,
formatTag.c_str());

return std::string{identifier};
}
Loading

0 comments on commit 5f64aa9

Please sign in to comment.