From d1e5ecf0714feeb0d30523d82e3bd12fb1d0f3dc Mon Sep 17 00:00:00 2001 From: CastagnaIT Date: Sat, 19 Aug 2023 09:24:01 +0200 Subject: [PATCH] [AdaptiveStream] Fix sidx/init parsing --- src/common/AdaptiveStream.cpp | 177 ++++++++++++++++++---------------- src/common/AdaptiveStream.h | 2 +- src/parser/DASHTree.cpp | 24 ++--- 3 files changed, 106 insertions(+), 97 deletions(-) diff --git a/src/common/AdaptiveStream.cpp b/src/common/AdaptiveStream.cpp index d87e9eeb5..d70d6911c 100644 --- a/src/common/AdaptiveStream.cpp +++ b/src/common/AdaptiveStream.cpp @@ -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& refs = sidx->GetReferences(); - AP4_SidxAtom* sidx(AP4_DYNAMIC_CAST(AP4_SidxAtom, atom)); - const AP4_Array& 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); @@ -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 lck(thread_data_->mutex_dl_); - if (!ResolveSegmentBase(current_rep_)) + if (!GenerateSidxSegments(current_rep_)) { state_ = STOPPED; return false; @@ -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) { @@ -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 sidxBuffer; DownloadInfo downloadInfo; diff --git a/src/common/AdaptiveStream.h b/src/common/AdaptiveStream.h index fdf32a392..1f555d03e 100644 --- a/src/common/AdaptiveStream.h +++ b/src/common/AdaptiveStream.h @@ -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 { diff --git a/src/parser/DASHTree.cpp b/src/parser/DASHTree.cpp index 8b534d3a3..1c1a1456c 100644 --- a/src/parser/DASHTree.cpp +++ b/src/parser/DASHTree.cpp @@ -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(); @@ -1071,21 +1074,12 @@ 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()) @@ -1093,7 +1087,7 @@ void adaptive::CDashTree::ParseTagRepresentation(pugi::xml_node nodeRepr, // 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());