diff --git a/src/Session.cpp b/src/Session.cpp index 085392961..cc0d285d6 100644 --- a/src/Session.cpp +++ b/src/Session.cpp @@ -1013,7 +1013,7 @@ void CSession::UpdateStream(CStream& stream) stream.m_info.SetCodecInternalName(codecStr); } -AP4_Movie* CSession::PrepareStream(CStream* stream, bool& needRefetch) +void CSession::PrepareStream(CStream* stream, bool& needRefetch) { needRefetch = false; CRepresentation* repr = stream->m_adStream.getRepresentation(); @@ -1022,10 +1022,10 @@ AP4_Movie* CSession::PrepareStream(CStream* stream, bool& needRefetch) stream->m_adStream.getAdaptationSet(), repr)) { case PrepareRepStatus::FAILURE: - return nullptr; + return; case PrepareRepStatus::DRMCHANGED: if (!InitializeDRM()) - return nullptr; + return; [[fallthrough]]; case PrepareRepStatus::DRMUNCHANGED: stream->m_isEncrypted = repr->m_psshSetPos != PSSHSET_POS_DEFAULT; @@ -1034,72 +1034,6 @@ AP4_Movie* CSession::PrepareStream(CStream* stream, bool& needRefetch) default: break; } - - if (repr->GetContainerType() == ContainerType::MP4 && !repr->HasInitPrefixed() && - !repr->HasInitSegment()) - { - //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_ - AP4_Movie* movie{new AP4_Movie()}; - - AP4_SyntheticSampleTable* sample_table{new AP4_SyntheticSampleTable()}; - - AP4_SampleDescription* sample_descryption; - const std::string& extradata = repr->GetCodecPrivateData(); - - if (stream->m_info.GetCodecName() == "h264") - { - AP4_MemoryByteStream ms{reinterpret_cast(extradata.data()), - static_cast(extradata.size())}; - AP4_AvccAtom* atom{AP4_AvccAtom::Create(AP4_ATOM_HEADER_SIZE + extradata.size(), ms)}; - sample_descryption = - new AP4_AvcSampleDescription(AP4_SAMPLE_FORMAT_AVC1, stream->m_info.GetWidth(), - stream->m_info.GetHeight(), 0, nullptr, atom); - } - else if (stream->m_info.GetCodecName() == "hevc") - { - AP4_MemoryByteStream ms{reinterpret_cast(extradata.data()), - static_cast(extradata.size())}; - AP4_HvccAtom* atom{AP4_HvccAtom::Create(AP4_ATOM_HEADER_SIZE + extradata.size(), ms)}; - sample_descryption = - new AP4_HevcSampleDescription(AP4_SAMPLE_FORMAT_HEV1, stream->m_info.GetWidth(), - stream->m_info.GetHeight(), 0, nullptr, atom); - } - else if (stream->m_info.GetCodecName() == "av1") - { - AP4_MemoryByteStream ms{reinterpret_cast(extradata.data()), - static_cast(extradata.size())}; - AP4_Av1cAtom* atom = AP4_Av1cAtom::Create(AP4_ATOM_HEADER_SIZE + extradata.size(), ms); - sample_descryption = - new AP4_Av1SampleDescription(AP4_SAMPLE_FORMAT_AV01, stream->m_info.GetWidth(), - stream->m_info.GetHeight(), 0, nullptr, atom); - } - else if (stream->m_info.GetCodecName() == "srt") - sample_descryption = new AP4_SampleDescription(AP4_SampleDescription::TYPE_SUBTITLES, - AP4_SAMPLE_FORMAT_STPP, 0); - else - sample_descryption = new AP4_SampleDescription(AP4_SampleDescription::TYPE_UNKNOWN, 0, 0); - - if (repr->GetPsshSetPos() != PSSHSET_POS_DEFAULT) - { - AP4_ContainerAtom schi{AP4_ATOM_TYPE_SCHI}; - schi.AddChild( - new AP4_TencAtom(AP4_CENC_CIPHER_AES_128_CTR, 8, - GetDefaultKeyId(repr->GetPsshSetPos()))); - sample_descryption = new AP4_ProtectedSampleDescription( - 0, sample_descryption, 0, AP4_PROTECTION_SCHEME_TYPE_PIFF, 0, "", &schi); - } - sample_table->AddSampleDescription(sample_descryption); - movie->AddTrack(new AP4_Track(static_cast(stream->m_adStream.GetTrackType()), - sample_table, CFragmentedSampleReader::TRACKID_UNKNOWN, - repr->GetTimescale(), 0, repr->GetTimescale(), 0, "", 0, 0)); - //Create a dumy MOOV Atom to tell Bento4 its a fragmented stream - AP4_MoovAtom* moov{new AP4_MoovAtom()}; - moov->AddChild(new AP4_ContainerAtom(AP4_ATOM_TYPE_MVEX)); - movie->SetMoovAtom(moov); - return movie; - } - return nullptr; } void CSession::EnableStream(CStream* stream, bool enable) @@ -1628,3 +1562,75 @@ bool CSession::SeekChapter(int ch) } return false; } + +AP4_Movie* CSession::CreateMovieAtom(CStream* stream) +{ + CRepresentation* repr = stream->m_adStream.getRepresentation(); + + if (repr->GetContainerType() == ContainerType::MP4 && !repr->HasInitSegment()) + { + AP4_SampleDescription* sampleDesc; + const std::string& extradata = repr->GetCodecPrivateData(); + + if (stream->m_info.GetCodecName() == CODEC::NAME_H264) + { + AP4_MemoryByteStream ms{reinterpret_cast(extradata.data()), + static_cast(extradata.size())}; + AP4_AvccAtom* atom{AP4_AvccAtom::Create(AP4_ATOM_HEADER_SIZE + extradata.size(), ms)}; + sampleDesc = new AP4_AvcSampleDescription(AP4_SAMPLE_FORMAT_AVC1, stream->m_info.GetWidth(), + stream->m_info.GetHeight(), 0, nullptr, atom); + } + else if (stream->m_info.GetCodecName() == CODEC::NAME_HEVC) + { + AP4_MemoryByteStream ms{reinterpret_cast(extradata.data()), + static_cast(extradata.size())}; + AP4_HvccAtom* atom{AP4_HvccAtom::Create(AP4_ATOM_HEADER_SIZE + extradata.size(), ms)}; + sampleDesc = new AP4_HevcSampleDescription(AP4_SAMPLE_FORMAT_HEV1, stream->m_info.GetWidth(), + stream->m_info.GetHeight(), 0, nullptr, atom); + } + else if (stream->m_info.GetCodecName() == CODEC::NAME_AV1) + { + AP4_MemoryByteStream ms{reinterpret_cast(extradata.data()), + static_cast(extradata.size())}; + AP4_Av1cAtom* atom = AP4_Av1cAtom::Create(AP4_ATOM_HEADER_SIZE + extradata.size(), ms); + sampleDesc = new AP4_Av1SampleDescription(AP4_SAMPLE_FORMAT_AV01, stream->m_info.GetWidth(), + stream->m_info.GetHeight(), 0, nullptr, atom); + } + else if (stream->m_info.GetCodecName() == CODEC::NAME_SRT) + { + sampleDesc = new AP4_SampleDescription(AP4_SampleDescription::TYPE_SUBTITLES, + AP4_SAMPLE_FORMAT_STPP, 0); + } + else + { + LOG::LogF(LOGWARNING, + "Created sample desciption atom of unknown type, codec \"%s\" is not handled", + stream->m_info.GetCodecName().c_str()); + sampleDesc = new AP4_SampleDescription(AP4_SampleDescription::TYPE_UNKNOWN, 0, 0); + } + + if (repr->GetPsshSetPos() != PSSHSET_POS_DEFAULT) + { + AP4_ContainerAtom schi{AP4_ATOM_TYPE_SCHI}; + schi.AddChild( + new AP4_TencAtom(AP4_CENC_CIPHER_AES_128_CTR, 8, GetDefaultKeyId(repr->GetPsshSetPos()))); + sampleDesc = new AP4_ProtectedSampleDescription( + 0, sampleDesc, 0, AP4_PROTECTION_SCHEME_TYPE_PIFF, 0, "", &schi); + } + + AP4_SyntheticSampleTable* sampleTable{new AP4_SyntheticSampleTable()}; + sampleTable->AddSampleDescription(sampleDesc); + + // Note: AP4_Movie ptr will be deleted from AP4_File destructor of CStream + AP4_Movie* movie{new AP4_Movie()}; + movie->AddTrack(new AP4_Track(static_cast(stream->m_adStream.GetTrackType()), + sampleTable, CFragmentedSampleReader::TRACKID_UNKNOWN, + repr->GetTimescale(), 0, repr->GetTimescale(), 0, "", 0, 0)); + // Create MOOV Atom to allow bento4 to handle stream as fragmented MP4 + AP4_MoovAtom* moov{new AP4_MoovAtom()}; + moov->AddChild(new AP4_ContainerAtom(AP4_ATOM_TYPE_MVEX)); + movie->SetMoovAtom(moov); + return movie; + } + return nullptr; +} diff --git a/src/Session.h b/src/Session.h index 4d2e0f37c..135cb744b 100644 --- a/src/Session.h +++ b/src/Session.h @@ -92,10 +92,8 @@ class ATTR_DLL_LOCAL CSession : public adaptive::AdaptiveStreamObserver /*! \brief Update stream's InputstreamInfo * \param stream The stream to prepare * \param needRefetch [OUT] Set to true if stream info has changed - * \return The Movie box if the stream container is MP4 and a valid MOOV - * box is found, otherwise nullptr */ - AP4_Movie* PrepareStream(CStream* stream, bool& needRefetch); + void PrepareStream(CStream* stream, bool& needRefetch); /*! \brief Get a stream by index (starting at 1) @@ -333,6 +331,13 @@ class ATTR_DLL_LOCAL CSession : public adaptive::AdaptiveStreamObserver */ void OnStreamChange(adaptive::AdaptiveStream* adStream) override; + /*! + * \brief Create a Movie (MOOV) atom from scratch based on manifest info. + * \param stream The stream where to get the info + * \return The Movie atom if success, otherwise nullptr + */ + AP4_Movie* CreateMovieAtom(CStream* stream); + protected: /*! * \brief Event raised when the current segment is changed and diff --git a/src/common/AdaptiveStream.cpp b/src/common/AdaptiveStream.cpp index d87e9eeb5..625f43e9f 100644 --- a/src/common/AdaptiveStream.cpp +++ b/src/common/AdaptiveStream.cpp @@ -436,6 +436,11 @@ void AdaptiveStream::OnTFRFatom(uint64_t ts, uint64_t duration, uint32_t mediaTi duration, mediaTimescale); } +bool adaptive::AdaptiveStream::IsRequiredCreateMovieAtom() +{ + return tree_.GetTreeType() == TreeType::SMOOTH_STREAMING; +} + bool AdaptiveStream::parseIndexRange(PLAYLIST::CRepresentation* rep, const std::vector& buffer) { @@ -483,87 +488,100 @@ bool AdaptiveStream::parseIndexRange(PLAYLIST::CRepresentation* rep, } else if (rep->GetContainerType() == ContainerType::MP4) { - if (rep->GetSegmentBase()->GetIndexRangeBegin() == 0) + uint64_t boxSize{0}; + uint64_t initRangeEnd{NO_VALUE}; + // Note: if the init segment is set, means that we have downloaded data starting from the IndexRangeBegin offset + // so we need to include the data size not downloaded to the begin range of first segment + if (rep->HasSegmentBase() && rep->HasInitSegment()) { - 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; + boxSize = rep->GetSegmentBase()->GetIndexRangeBegin(); + initRangeEnd = boxSize - 1; } + bool isMoovFound{false}; + 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))) - { - LOG::Log(LOGERROR, "[AS-%u] Unable to create SIDX from IndexRange bytes", clsId); - return false; - } + AP4_Position streamPos{0}; // Current stream position (offset where ends the current box) + byteStream.Tell(streamPos); - if (atom->GetType() == AP4_ATOM_TYPE_MOOF) + if (atom->GetType() == AP4_ATOM_TYPE_MOOV) { + isMoovFound = true; + initRangeEnd = streamPos - 1; delete atom; - break; } - else if (atom->GetType() != AP4_ATOM_TYPE_SIDX) + else if (atom->GetType() == AP4_ATOM_TYPE_MOOF || atom->GetType() == AP4_ATOM_TYPE_MDAT) { + // Stop iteration because media segments are started delete atom; - continue; + break; } - - AP4_SidxAtom* sidx(AP4_DYNAMIC_CAST(AP4_SidxAtom, atom)); - const AP4_Array& refs(sidx->GetReferences()); - - if (refs[0].m_ReferenceType == 1) + else if (atom->GetType() == AP4_ATOM_TYPE_SIDX && sidxCount > 0) { - numSIDX = refs.ItemCount(); - delete atom; - continue; - } + AP4_SidxAtom* sidx = AP4_DYNAMIC_CAST(AP4_SidxAtom, atom); + const AP4_Array& refs = sidx->GetReferences(); - AP4_Position pos; - byteStream.Tell(pos); - seg.range_end_ = - pos + rep->GetSegmentBase()->GetIndexRangeBegin() + sidx->GetFirstOffset() - 1; - rep->SetTimescale(sidx->GetTimeScale()); - rep->SetScaling(); + 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; + } - 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); + rep->SetTimescale(sidx->GetTimeScale()); + rep->SetScaling(); - if (adpSet->SegmentTimelineDuration().GetSize() < rep->SegmentTimeline().GetSize()) + seg.range_end_ = streamPos + boxSize + sidx->GetFirstOffset() - 1; + + 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 init segment, missing MOOV atom in stream", + clsId); + return false; + } + if (initRangeEnd == NO_VALUE) + { + LOG::LogF(LOGERROR, "[AS-%u] Cannot create init segment, cannot determinate range end", + 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 +639,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 +882,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 +1200,46 @@ 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 + if (rep->GetContainerType() != ContainerType::MP4 && + rep->GetContainerType() != ContainerType::WEBM) + { + LOG::LogF(LOGERROR, + "[AS-%u] Cannot generate segments from SIDX on repr id \"%s\" with container \"%i\"", + clsId, rep->GetId().data(), static_cast(rep->GetContainerType())); + return false; + } - auto& segBase = rep->GetSegmentBase(); + // Get the byte ranges to download the index segment to generate media segments from SIDX atom CSegment seg; + // SetIsInitialization is set just to ignore fileOffset on PrepareDownload + // the init segment will be set to representation by ParseIndexRange + seg.SetIsInitialization(true); - 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 (segBase->GetIndexRangeEnd() > 0) + { + // No init segment, we need to create it, so get all bytes from start to try get MOOV atom + seg.range_begin_ = rep->HasInitSegment() ? segBase->GetIndexRangeBegin() : 0; + 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..b681cd41f 100644 --- a/src/common/AdaptiveStream.h +++ b/src/common/AdaptiveStream.h @@ -91,6 +91,13 @@ class AdaptiveStream; void OnTFRFatom(uint64_t ts, uint64_t duration, uint32_t mediaTimescale) override; + /*! + * \brief Some streaming manifest types can be required to create the Movie (MOOV) atom + * because its not provided with the stream. + * \return True if its required to create Movie (MOOV) atom, otherwise false + */ + bool IsRequiredCreateMovieAtom(); + protected: virtual bool parseIndexRange(PLAYLIST::CRepresentation* rep, const std::vector& buffer); @@ -177,7 +184,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/common/AdaptiveTree.cpp b/src/common/AdaptiveTree.cpp index d781c87ac..54e1ba935 100644 --- a/src/common/AdaptiveTree.cpp +++ b/src/common/AdaptiveTree.cpp @@ -9,6 +9,7 @@ #include "AdaptiveTree.h" #include "Chooser.h" +#include "../common/AdaptiveUtils.h" #include "../utils/FileUtils.h" #include "../utils/StringUtils.h" #include "../utils/UrlUtils.h" diff --git a/src/common/AdaptiveTree.h b/src/common/AdaptiveTree.h index e4e960c2a..339c449d3 100644 --- a/src/common/AdaptiveTree.h +++ b/src/common/AdaptiveTree.h @@ -37,10 +37,23 @@ namespace CHOOSER { class IRepresentationChooser; } +namespace PLAYLIST +{ +enum class TreeType; +} namespace adaptive { +// \brief Adaptive tree types +enum class TreeType +{ + UNKNOWN, + DASH, + HLS, + SMOOTH_STREAMING +}; + class ATTR_DLL_LOCAL AdaptiveTree { public: @@ -74,6 +87,8 @@ class ATTR_DLL_LOCAL AdaptiveTree AdaptiveTree(const AdaptiveTree& left); virtual ~AdaptiveTree() = default; + virtual TreeType GetTreeType() { return TreeType::UNKNOWN; } + /*! * \brief Configure the adaptive tree. * \param kodiProps The Kodi properties diff --git a/src/common/Representation.h b/src/common/Representation.h index 8552bca43..ebaa1d190 100644 --- a/src/common/Representation.h +++ b/src/common/Representation.h @@ -136,9 +136,6 @@ class ATTR_DLL_LOCAL CRepresentation : public CCommonSegAttribs, public CCommonA m_isSubtitleFileStream = isSubtitleFileStream; } - bool HasInitPrefixed() const { return m_hasInitPrefixed; } - void SetHasInitPrefixed(bool hasInitPrefixed) { m_hasInitPrefixed = hasInitPrefixed; } - // Currently used for HLS only bool IsPrepared() const { return m_isPrepared; } // Currently used for HLS only @@ -289,7 +286,6 @@ class ATTR_DLL_LOCAL CRepresentation : public CCommonSegAttribs, public CCommonA uint32_t m_timescale{0}; bool m_isSubtitleFileStream{false}; - bool m_hasInitPrefixed{false}; bool m_isPrepared{false}; bool m_isEnabled{false}; diff --git a/src/main.cpp b/src/main.cpp index 1c4acfdc3..f424ade5b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -279,7 +279,7 @@ bool CInputStreamAdaptive::OpenStream(int streamid) return stream->GetReader()->GetInformation(stream->m_info); } - AP4_Movie* movie(m_session->PrepareStream(stream, needRefetch)); + m_session->PrepareStream(stream, needRefetch); stream->m_adStream.start_stream(); @@ -338,14 +338,19 @@ bool CInputStreamAdaptive::OpenStream(int streamid) } else if (reprContainerType == ContainerType::MP4) { + AP4_Movie* movie{nullptr}; + if (stream->m_adStream.IsRequiredCreateMovieAtom()) + movie = m_session->CreateMovieAtom(stream); + stream->SetAdByteStream(std::make_unique(&stream->m_adStream)); + // When "movie" is nullptr, AP4_File tries to extract it from the stream stream->SetStreamFile(std::make_unique( *stream->GetAdByteStream(), AP4_DefaultAtomFactory::Instance_, true, movie)); movie = stream->GetStreamFile()->GetMovie(); - if (movie == NULL) + if (!movie) { - LOG::Log(LOGERROR, "No MOOV in stream!"); + LOG::LogF(LOGERROR, "No MOOV atom in stream"); m_session->EnableStream(stream, false); return false; } @@ -358,7 +363,7 @@ bool CInputStreamAdaptive::OpenStream(int streamid) track = movie->GetTrack(AP4_Track::TYPE_TEXT); if (!track) { - LOG::Log(LOGERROR, "No suitable track found in stream"); + LOG::LogF(LOGERROR, "No suitable Track atom found in stream"); m_session->EnableStream(stream, false); return false; } diff --git a/src/parser/DASHTree.cpp b/src/parser/DASHTree.cpp index 8b534d3a3..975fcebe4 100644 --- a/src/parser/DASHTree.cpp +++ b/src/parser/DASHTree.cpp @@ -990,7 +990,7 @@ void adaptive::CDashTree::ParseTagRepresentation(pugi::xml_node nodeRepr, } // Generate timeline segments - if (!repr->HasSegmentTimeline() && repr->HasSegmentTemplate()) + if (repr->HasSegmentTemplate()) { auto& segTemplate = repr->GetSegmentTemplate(); @@ -1071,27 +1071,6 @@ 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 (?) - //! 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 diff --git a/src/parser/DASHTree.h b/src/parser/DASHTree.h index 3006af933..6a91b90d9 100644 --- a/src/parser/DASHTree.h +++ b/src/parser/DASHTree.h @@ -32,6 +32,8 @@ class ATTR_DLL_LOCAL CDashTree : public adaptive::AdaptiveTree CDashTree() : AdaptiveTree() {} CDashTree(const CDashTree& left); + virtual TreeType GetTreeType() override { return TreeType::DASH; } + virtual bool Open(std::string_view url, const std::map& headers, const std::string& data) override; diff --git a/src/parser/HLSTree.h b/src/parser/HLSTree.h index 62431f686..91fe0b3ac 100644 --- a/src/parser/HLSTree.h +++ b/src/parser/HLSTree.h @@ -24,6 +24,8 @@ class ATTR_DLL_LOCAL CHLSTree : public AdaptiveTree CHLSTree() : AdaptiveTree() {} virtual ~CHLSTree() {} + virtual TreeType GetTreeType() override { return TreeType::HLS; } + CHLSTree(const CHLSTree& left); virtual CHLSTree* Clone() const override { return new CHLSTree{*this}; } diff --git a/src/parser/SmoothTree.h b/src/parser/SmoothTree.h index 37652c7c7..3e8611c7c 100644 --- a/src/parser/SmoothTree.h +++ b/src/parser/SmoothTree.h @@ -26,6 +26,8 @@ class ATTR_DLL_LOCAL CSmoothTree : public AdaptiveTree CSmoothTree() : AdaptiveTree() {} CSmoothTree(const CSmoothTree& left); + virtual TreeType GetTreeType() override { return TreeType::SMOOTH_STREAMING; } + virtual bool Open(std::string_view url, const std::map& headers, const std::string& data) override;