Skip to content

Commit

Permalink
[AdaptiveStream] Fix sidx/init parsing
Browse files Browse the repository at this point in the history
  • Loading branch information
CastagnaIT committed Aug 19, 2023
1 parent 59c3274 commit d1e5ecf
Show file tree
Hide file tree
Showing 3 changed files with 106 additions and 97 deletions.
177 changes: 96 additions & 81 deletions src/common/AdaptiveStream.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -483,87 +483,92 @@ bool AdaptiveStream::parseIndexRange(PLAYLIST::CRepresentation* rep,
}
else if (rep->GetContainerType() == ContainerType::MP4)
{
if (rep->GetSegmentBase()->GetIndexRangeBegin() == 0)
uint64_t indexRangeBegin{0};
uint64_t initRangeEnd{NO_VALUE};
if (rep->HasSegmentBase())
{
AP4_File fileStream{byteStream, AP4_DefaultAtomFactory::Instance_, true};
AP4_Movie* movie{fileStream.GetMovie()};

if (movie == nullptr)
{
LOG::Log(LOGERROR, "[AS-%u] No MOOV in stream!", clsId);
return false;
}

if (!rep->HasInitSegment())
{
LOG::LogF(LOGERROR, "[AS-%u] Representation has no init segment", clsId);
return false;
}
rep->GetInitSegment()->range_begin_ = 0;
AP4_Position pos;
byteStream.Tell(pos);
rep->GetInitSegment()->range_end_ = pos - 1;
indexRangeBegin = rep->GetSegmentBase()->GetIndexRangeBegin();
initRangeEnd = indexRangeBegin - 1;
}

bool isMoovFound{false};
AP4_Position streamPos{0};
AP4_Cardinal sidxCount{1};
uint64_t reprDuration{0};

CSegment seg;
seg.startPTS_ = 0;
AP4_Cardinal numSIDX{1};
uint64_t reprDuration{0};

while (numSIDX > 0)
// Iterate each atom in the stream
AP4_DefaultAtomFactory atomFactory;
AP4_Atom* atom{nullptr};
while (AP4_SUCCEEDED(atomFactory.CreateAtomFromStream(byteStream, atom)))
{
AP4_Atom* atom{nullptr};
if (AP4_FAILED(AP4_DefaultAtomFactory::Instance_.CreateAtomFromStream(byteStream, atom)))
byteStream.Tell(streamPos); // Get the stream pos (offset where ends of the current box)
if (atom->GetType() == AP4_ATOM_TYPE_MOOV)
{
LOG::Log(LOGERROR, "[AS-%u] Unable to create SIDX from IndexRange bytes", clsId);
return false;
isMoovFound = true;
if (initRangeEnd == NO_VALUE)
initRangeEnd = streamPos - 1;
delete atom;
}

if (atom->GetType() == AP4_ATOM_TYPE_MOOF)
else if (atom->GetType() == AP4_ATOM_TYPE_MOOF || atom->GetType() == AP4_ATOM_TYPE_MDAT)
{
// Stop iteration because media segments are started
delete atom;
break;
}
else if (atom->GetType() != AP4_ATOM_TYPE_SIDX)
else if (atom->GetType() == AP4_ATOM_TYPE_SIDX && sidxCount > 0)
{
delete atom;
continue;
}
AP4_SidxAtom* sidx = AP4_DYNAMIC_CAST(AP4_SidxAtom, atom);
const AP4_Array<AP4_SidxAtom::Reference>& refs = sidx->GetReferences();

AP4_SidxAtom* sidx(AP4_DYNAMIC_CAST(AP4_SidxAtom, atom));
const AP4_Array<AP4_SidxAtom::Reference>& refs(sidx->GetReferences());

if (refs[0].m_ReferenceType == 1)
{
numSIDX = refs.ItemCount();
delete atom;
continue;
}
if (refs[0].m_ReferenceType == 1) // type 1 ref to a sidx box, type 0 ref to a moof box
{
sidxCount = refs.ItemCount();
delete atom;
continue;
}

AP4_Position pos;
byteStream.Tell(pos);
seg.range_end_ =
pos + rep->GetSegmentBase()->GetIndexRangeBegin() + sidx->GetFirstOffset() - 1;
rep->SetTimescale(sidx->GetTimeScale());
rep->SetScaling();
rep->SetTimescale(sidx->GetTimeScale());
rep->SetScaling();

for (AP4_Cardinal i{0}; i < refs.ItemCount(); i++)
{
seg.range_begin_ = seg.range_end_ + 1;
seg.range_end_ = seg.range_begin_ + refs[i].m_ReferencedSize - 1;
rep->SegmentTimeline().GetData().emplace_back(seg);
seg.range_end_ = streamPos + indexRangeBegin + sidx->GetFirstOffset() - 1;

if (adpSet->SegmentTimelineDuration().GetSize() < rep->SegmentTimeline().GetSize())
for (AP4_Cardinal i{0}; i < refs.ItemCount(); i++)
{
adpSet->SegmentTimelineDuration().GetData().emplace_back(refs[i].m_SubsegmentDuration);
seg.range_begin_ = seg.range_end_ + 1;
seg.range_end_ = seg.range_begin_ + refs[i].m_ReferencedSize - 1;
rep->SegmentTimeline().GetData().emplace_back(seg);

if (adpSet->SegmentTimelineDuration().GetSize() < rep->SegmentTimeline().GetSize())
{
adpSet->SegmentTimelineDuration().GetData().emplace_back(refs[i].m_SubsegmentDuration);
}

seg.startPTS_ += refs[i].m_SubsegmentDuration;
reprDuration += refs[i].m_SubsegmentDuration;
}

seg.startPTS_ += refs[i].m_SubsegmentDuration;
reprDuration += refs[i].m_SubsegmentDuration;
sidxCount--;
delete atom;
}
}

delete atom;
numSIDX--;
if (!rep->HasInitSegment())
{
if (!isMoovFound)
{
LOG::LogF(LOGERROR, "[AS-%u] Cannot create initialization segment, missing MOOV in stream",
clsId);
return false;
}
// Create the initialization segment
CSegment initSeg;
initSeg.SetIsInitialization(true);
initSeg.range_begin_ = 0;
initSeg.range_end_ = initRangeEnd;
rep->SetInitSegment(initSeg);
}

rep->SetDuration(reprDuration);
Expand Down Expand Up @@ -621,11 +626,11 @@ bool AdaptiveStream::start_stream()
thread_data_->signal_dl_.wait(lckdl);
}

if (current_rep_->SegmentTimeline().IsEmpty() && current_rep_->HasSegmentBase())
if (current_rep_->SegmentTimeline().IsEmpty() && !current_rep_->IsSubtitleFileStream())
{
// ResolveSegmentbase assumes mutex_dl locked
// GenerateSidxSegments assumes mutex_dl locked
std::lock_guard<std::mutex> lck(thread_data_->mutex_dl_);
if (!ResolveSegmentBase(current_rep_))
if (!GenerateSidxSegments(current_rep_))
{
state_ = STOPPED;
return false;
Expand Down Expand Up @@ -864,8 +869,8 @@ bool AdaptiveStream::ensureSegment()
}

// If the representation has been changed, segments may have to be generated (DASH)
if (newRep->SegmentTimeline().IsEmpty() && newRep->HasSegmentBase())
ResolveSegmentBase(newRep);
if (newRep->SegmentTimeline().IsEmpty() && !newRep->IsSubtitleFileStream())
GenerateSidxSegments(newRep);

if (!newRep->IsPrepared() && tree_.SecondsSinceRepUpdate(newRep) > 1)
{
Expand Down Expand Up @@ -1182,32 +1187,42 @@ void AdaptiveStream::FixateInitialization(bool on)
m_fixateInitialization = on && current_rep_->HasInitSegment();
}

bool AdaptiveStream::ResolveSegmentBase(PLAYLIST::CRepresentation* rep)
bool AdaptiveStream::GenerateSidxSegments(PLAYLIST::CRepresentation* rep)
{
// Get the byte ranges to download the index segment to generate media segments from SIDX atom

auto& segBase = rep->GetSegmentBase();
CSegment seg;

if (!rep->HasInitSegment() && segBase->GetIndexRangeBegin() == 0 &&
segBase->GetIndexRangeEnd() > 0)
if (rep->HasSegmentBase())
{
seg.SetIsInitialization(true);
seg.range_end_ = segBase->GetIndexRangeEnd();
// Initialization segment will be set to representation by ParseIndexRange
}
else if (segBase->GetIndexRangeBegin() > 0 || !rep->HasInitSegment())
{
// It's no an initialization segment
seg.range_begin_ = segBase->GetIndexRangeBegin();
seg.range_end_ = segBase->GetIndexRangeEnd();
auto& segBase = rep->GetSegmentBase();
if (!rep->HasInitSegment() && segBase->GetIndexRangeBegin() == 0 &&
segBase->GetIndexRangeEnd() > 0)
{
// SetIsInitialization is set just to ignore fileOffset on PrepareDownload
// the init segment will be set to representation by ParseIndexRange
seg.SetIsInitialization(true);
seg.range_begin_ = 0;
seg.range_end_ = segBase->GetIndexRangeEnd();
}
else if (segBase->GetIndexRangeBegin() > 0 || !rep->HasInitSegment())
{
seg.range_begin_ = segBase->GetIndexRangeBegin();
seg.range_end_ = segBase->GetIndexRangeEnd();
}
else if (rep->HasInitSegment())
{
seg = *rep->GetInitSegment();
}
else
return false;
}
else if (rep->HasInitSegment())
else
{
seg = *rep->GetInitSegment();
// We dont know the range positions for the index segment
static const uint64_t indexRangeEnd = 1024 * 200;
seg.range_begin_ = 0;
seg.range_end_ = indexRangeEnd;
}
else
return false;

std::vector<uint8_t> sidxBuffer;
DownloadInfo downloadInfo;
Expand Down
2 changes: 1 addition & 1 deletion src/common/AdaptiveStream.h
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ class AdaptiveStream;

int SecondsSinceUpdate() const;
static void ReplacePlaceholder(std::string& url, const std::string placeholder, uint64_t value);
bool ResolveSegmentBase(PLAYLIST::CRepresentation* rep);
bool GenerateSidxSegments(PLAYLIST::CRepresentation* rep);

struct THREADDATA
{
Expand Down
24 changes: 9 additions & 15 deletions src/parser/DASHTree.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -990,7 +990,10 @@ void adaptive::CDashTree::ParseTagRepresentation(pugi::xml_node nodeRepr,
}

// Generate timeline segments
if (!repr->HasSegmentTimeline() && repr->HasSegmentTemplate())
if (!repr->HasSegmentTimeline())
{
}
if (repr->HasSegmentTemplate())
{
auto& segTemplate = repr->GetSegmentTemplate();

Expand Down Expand Up @@ -1071,29 +1074,20 @@ void adaptive::CDashTree::ParseTagRepresentation(pugi::xml_node nodeRepr,
"Cannot generate segments timeline, the segment count exceeds SIDX atom limit.");
}
}
else if (!repr->HasSegmentBase() && !repr->IsSubtitleFileStream())
{
//Let us try to extract the fragments out of SIDX atom
CSegmentBase segBase;

segBase.SetIndexRangeBegin(0);
//! @todo: Explain the reason of these specific values
static const uint64_t indexRangeMax = 1024 * 200;
segBase.SetIndexRangeEnd(indexRangeMax);

repr->SetSegmentBase(segBase);
}
}

//! @todo: "init prefixed" behaviour is not so clear and may be invalidated by ResolveSegmentBase (?)
//TODO!
repr->SetHasInitPrefixed(true);
/*
//! @todo: "init prefixed" behaviour is not so clear and may be invalidated by GenerateSidxSegments (?)
//! we need to investigate if we can move this check where its used by CSession::PrepareStream
//! and remove HasInitPrefixed statement
if (repr->HasInitSegment() && !repr->SegmentTimeline().IsEmpty())
{
// we assume that we have a MOOV atom included in each segment (max 100k = youtube)
repr->SetHasInitPrefixed(true);
}

*/
// Sanitize period
if (period->GetTimescale() == 0)
period->SetTimescale(repr->GetTimescale());
Expand Down

0 comments on commit d1e5ecf

Please sign in to comment.