From 1c08ea53a54efe754f0cc0c74200dd1322814cb1 Mon Sep 17 00:00:00 2001 From: Dirk Simonis Date: Fri, 11 Jun 2021 15:35:58 +1200 Subject: [PATCH 01/11] License renewal for widevine --- wvdecrypter/cdm/media/cdm/cdm_adapter.cc | 34 +++++++++++++++++++++--- wvdecrypter/wvdecrypter.cpp | 6 +++-- 2 files changed, 35 insertions(+), 5 deletions(-) diff --git a/wvdecrypter/cdm/media/cdm/cdm_adapter.cc b/wvdecrypter/cdm/media/cdm/cdm_adapter.cc index cc3bde285..873a4afd2 100644 --- a/wvdecrypter/cdm/media/cdm/cdm_adapter.cc +++ b/wvdecrypter/cdm/media/cdm/cdm_adapter.cc @@ -5,6 +5,7 @@ #include "cdm_adapter.h" #include #include +#include #define DCHECK(condition) assert(condition) @@ -63,10 +64,23 @@ void* GetCdmHost(int host_interface_version, void* user_data) } // namespace +std::atomic exit_thread_flag; +std::atomic timer_thread_running; + void timerfunc(std::shared_ptr adp, uint64_t delay, void* context) { - std::this_thread::sleep_for(std::chrono::milliseconds(delay)); - adp->TimerExpired(context); + timer_thread_running = true; + uint64_t waited = 0; + while (!exit_thread_flag && delay > waited) + { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + waited += 100; + } + if (!exit_thread_flag) + { + adp->TimerExpired(context); + } + timer_thread_running = false; } cdm::AudioDecoderConfig_1 ToAudioDecoderConfig_1( @@ -128,6 +142,11 @@ CdmAdapter::CdmAdapter( CdmAdapter::~CdmAdapter() { + exit_thread_flag = true; + while (timer_thread_running) + { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } if (cdm9_) cdm9_->Destroy(), cdm9_ = nullptr; else if (cdm10_) @@ -144,6 +163,8 @@ CdmAdapter::~CdmAdapter() void CdmAdapter::Initialize() { + exit_thread_flag = false; + timer_thread_running = false; if (cdm9_ || cdm10_ || cdm11_) { if (cdm9_) @@ -307,6 +328,11 @@ void CdmAdapter::CloseSession(uint32_t promise_id, const char* session_id, uint32_t session_id_size) { + exit_thread_flag = true; + while (timer_thread_running) + { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } if (cdm9_) cdm9_->CloseSession(promise_id, session_id, session_id_size); else if (cdm10_) @@ -475,7 +501,9 @@ cdm::Buffer* CdmAdapter::Allocate(uint32_t capacity) void CdmAdapter::SetTimer(int64_t delay_ms, void* context) { - //LICENSERENEWAL std::thread(timerfunc, shared_from_this(), delay_ms, context).detach(); + //LICENSERENEWAL + exit_thread_flag = false; + std::thread(timerfunc, shared_from_this(), delay_ms, context).detach(); } cdm::Time CdmAdapter::GetCurrentWallTime() diff --git a/wvdecrypter/wvdecrypter.cpp b/wvdecrypter/wvdecrypter.cpp index 84a8accfd..d51065c7f 100644 --- a/wvdecrypter/wvdecrypter.cpp +++ b/wvdecrypter/wvdecrypter.cpp @@ -1200,7 +1200,8 @@ AP4_Result WV_CencSingleSampleDecrypter::DecryptSampleData(AP4_UI32 pool_id, CdmDecryptedBlock cdm_out; cdm_out.SetDecryptedBuffer(&buf); - //LICENSERENEWAL: CheckLicenseRenewal(); + //LICENSERENEWAL: + CheckLicenseRenewal(); cdm::Status ret = drm_.GetCdmAdapter()->Decrypt(cdm_in, &cdm_out); if (ret == cdm::Status::kSuccess && useSingleDecrypt) @@ -1296,7 +1297,8 @@ SSD_DECODE_RETVAL WV_CencSingleSampleDecrypter::DecodeVideo(void* hostInstance, drained_ = false; //DecryptAndDecode calls Alloc wich cals kodi VideoCodec. Set instance handle. - //LICENSERENEWAL: CheckLicenseRenewal(); + //LICENSERENEWAL: + CheckLicenseRenewal(); media::CdmVideoFrame frame; cdm::Status ret = drm_.DecryptAndDecodeFrame(hostInstance, cdm_in, &frame); From e8ef39f7b973748dce43fd7e729acbdde6682e16 Mon Sep 17 00:00:00 2001 From: Matt Huisman Date: Fri, 11 Jun 2021 15:40:49 +1200 Subject: [PATCH 02/11] [DASH] fix for minimumUpdatePeriod=0 --- src/parser/DASHTree.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/parser/DASHTree.cpp b/src/parser/DASHTree.cpp index 7b6c84234..f5302c9fd 100644 --- a/src/parser/DASHTree.cpp +++ b/src/parser/DASHTree.cpp @@ -1033,6 +1033,10 @@ static void XMLCALL start(void* data, const char* el, const char** attr) { uint64_t dur(0); AddDuration((const char*)*(attr + 1), dur, 1500); + // 0S minimumUpdatePeriod = refresh after every segment + // We already do that so lets set our minimum updateInterval to 30s + if (dur == 0) + dur = 30000; dash->SetUpdateInterval(static_cast(dur)); } attr += 2; From b55a47eafb467a75b4bddc85195b7956bfa73bf5 Mon Sep 17 00:00:00 2001 From: Matt Huisman Date: Fri, 11 Jun 2021 15:41:30 +1200 Subject: [PATCH 03/11] fix gitignore --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 8977424a2..44fadd940 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ # build artifacts -./build/ +build/ debian/changelog debian/files debian/kodi-inputstream-mpd-dbg.debhelper.log From e6567420517e042e9b1968a341dcee1ba9a2ed39 Mon Sep 17 00:00:00 2001 From: Matt Huisman Date: Fri, 11 Jun 2021 15:43:38 +1200 Subject: [PATCH 04/11] [HLS] support webvtt subtitle extensions --- src/parser/HLSTree.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/parser/HLSTree.cpp b/src/parser/HLSTree.cpp index c68ecd48a..37d5cef04 100644 --- a/src/parser/HLSTree.cpp +++ b/src/parser/HLSTree.cpp @@ -487,7 +487,8 @@ HLSTree::PREPARE_RESULT HLSTree::prepareRepresentation(Period* period, rep->containerType_ = CONTAINERTYPE_ADTS; else if (strncmp(line.c_str() + ext, ".mp4", 4) == 0) rep->containerType_ = CONTAINERTYPE_MP4; - else if (strncmp(line.c_str() + ext, ".vtt", 4) == 0) + else if (strncmp(line.c_str() + ext, ".vtt", 4) == 0 || + strncmp(line.c_str() + ext, ".webvtt", 7) == 0) rep->containerType_ = CONTAINERTYPE_TEXT; else { From 7a186d810362e5aedb3dfdf0e2b6eb0aaf275c68 Mon Sep 17 00:00:00 2001 From: Matt Huisman Date: Fri, 11 Jun 2021 15:45:45 +1200 Subject: [PATCH 05/11] [DASH] manifest_update_parameter property defaults to 'full' for SegmentTimeline --- src/parser/DASHTree.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/parser/DASHTree.cpp b/src/parser/DASHTree.cpp index f5302c9fd..4b59b49d9 100644 --- a/src/parser/DASHTree.cpp +++ b/src/parser/DASHTree.cpp @@ -554,6 +554,9 @@ static void XMLCALL start(void* data, const char* el, const char** attr) { dash->currentNode_ |= MPDNODE_SEGMENTTIMELINE; dash->adp_timelined_ = true; + + if (dash->update_parameter_.empty() && dash->has_timeshift_buffer_) + dash->update_parameter_ = "full"; } } else if (dash->currentNode_ & MPDNODE_SEGMENTDURATIONS) From a8923eb0760f304180c76f1c2ebdc8d039cc4673 Mon Sep 17 00:00:00 2001 From: kszaq <737984+kszaq@users.noreply.github.com> Date: Fri, 11 Jun 2021 15:48:54 +1200 Subject: [PATCH 06/11] Fix decode failures on some Widevine content --- wvdecrypter/wvdecrypter.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/wvdecrypter/wvdecrypter.cpp b/wvdecrypter/wvdecrypter.cpp index d51065c7f..17fcfe5e3 100644 --- a/wvdecrypter/wvdecrypter.cpp +++ b/wvdecrypter/wvdecrypter.cpp @@ -1141,7 +1141,11 @@ AP4_Result WV_CencSingleSampleDecrypter::DecryptSampleData(AP4_UI32 pool_id, bool useSingleDecrypt(false); - if ((fragInfo.decrypter_flags_ & SSD_DECRYPTER::SSD_CAPS::SSD_SINGLE_DECRYPT) != 0 && subsample_count > 1) + // CDM should get 1 block of encrypted data per sample, encrypted data + // from all subsamples should be formed into a contiguous block. + // Even if there is only 1 subsample, we should remove cleartext data + // from it before passing to CDM. + if ((fragInfo.decrypter_flags_ & SSD_DECRYPTER::SSD_CAPS::SSD_SINGLE_DECRYPT) != 0) { decrypt_in_.Reserve(data_in.GetDataSize()); decrypt_in_.SetDataSize(0); From 4a319f91b27e8c8c7d467675c35a520987bd18c2 Mon Sep 17 00:00:00 2001 From: Matt Huisman Date: Fri, 11 Jun 2021 15:52:21 +1200 Subject: [PATCH 07/11] [DASH] fix segmentTemplate calculation --- src/common/AdaptiveTree.h | 1 + src/parser/DASHTree.cpp | 27 +++++++++++++++------------ 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/src/common/AdaptiveTree.h b/src/common/AdaptiveTree.h index 8cb51dad1..3ca34dd99 100644 --- a/src/common/AdaptiveTree.h +++ b/src/common/AdaptiveTree.h @@ -171,6 +171,7 @@ class AdaptiveTree std::string codecs_; std::string codec_private_data_; std::string source_url_; + std::string base_url_; uint32_t bandwidth_; uint32_t samplingRate_; uint16_t width_, height_; diff --git a/src/parser/DASHTree.cpp b/src/parser/DASHTree.cpp index 4b59b49d9..8eb401af4 100644 --- a/src/parser/DASHTree.cpp +++ b/src/parser/DASHTree.cpp @@ -89,9 +89,9 @@ static uint8_t GetChannels(const char** attr) static unsigned int ParseSegmentTemplate(const char** attr, std::string baseURL, std::string baseDomain, - DASHTree::SegmentTemplate& tpl) + DASHTree::SegmentTemplate& tpl, + unsigned int startNumber) { - unsigned int startNumber(1); for (; *attr;) { if (strcmp((const char*)*attr, "timescale") == 0) @@ -481,9 +481,9 @@ static void XMLCALL start(void* data, const char* el, const char** attr) { dash->current_representation_->segtpl_ = dash->current_adaptationset_->segtpl_; - dash->current_representation_->startNumber_ = - ParseSegmentTemplate(attr, dash->current_representation_->url_, dash->base_domain_, - dash->current_representation_->segtpl_); + dash->current_representation_->startNumber_ = ParseSegmentTemplate( + attr, dash->current_representation_->base_url_, dash->base_domain_, + dash->current_representation_->segtpl_, dash->current_adaptationset_->startNumber_); ReplacePlaceHolders(dash->current_representation_->segtpl_.media, dash->current_representation_->id, dash->current_representation_->bandwidth_); @@ -603,9 +603,9 @@ static void XMLCALL start(void* data, const char* el, const char** attr) } else if (strcmp(el, "SegmentTemplate") == 0) { - dash->current_adaptationset_->startNumber_ = - ParseSegmentTemplate(attr, dash->current_adaptationset_->base_url_, - dash->base_domain_, dash->current_adaptationset_->segtpl_); + dash->current_adaptationset_->startNumber_ = ParseSegmentTemplate( + attr, dash->current_adaptationset_->base_url_, dash->base_domain_, + dash->current_adaptationset_->segtpl_, dash->current_adaptationset_->startNumber_); dash->current_adaptationset_->timescale_ = dash->current_adaptationset_->segtpl_.timescale; dash->currentNode_ |= MPDNODE_SEGMENTTEMPLATE; @@ -657,13 +657,14 @@ static void XMLCALL start(void* data, const char* el, const char** attr) dash->current_representation_->timescale_ = dash->current_adaptationset_->timescale_; dash->current_representation_->duration_ = dash->current_adaptationset_->duration_; dash->current_representation_->startNumber_ = dash->current_adaptationset_->startNumber_; - dash->current_adaptationset_->representations_.push_back(dash->current_representation_); dash->current_representation_->width_ = dash->adpwidth_; dash->current_representation_->height_ = dash->adpheight_; dash->current_representation_->fpsRate_ = dash->adpfpsRate_; dash->current_representation_->fpsScale_ = dash->adpfpsScale_; dash->current_representation_->aspect_ = dash->adpaspect_; dash->current_representation_->containerType_ = dash->adpContainerType_; + dash->current_representation_->base_url_ = dash->current_adaptationset_->base_url_; + dash->current_adaptationset_->representations_.push_back(dash->current_representation_); dash->current_pssh_.clear(); dash->current_hasRepURN_ = false; @@ -939,9 +940,9 @@ static void XMLCALL start(void* data, const char* el, const char** attr) } else if (strcmp(el, "SegmentTemplate") == 0) { - dash->current_period_->startNumber_ = - ParseSegmentTemplate(attr, dash->current_period_->base_url_, dash->base_domain_, - dash->current_period_->segtpl_); + dash->current_period_->startNumber_ = ParseSegmentTemplate( + attr, dash->current_period_->base_url_, dash->base_domain_, + dash->current_period_->segtpl_, dash->current_period_->startNumber_); dash->current_period_->timescale_ = dash->current_period_->segtpl_.timescale; dash->currentNode_ |= MPDNODE_SEGMENTTEMPLATE; } @@ -1101,6 +1102,8 @@ static void XMLCALL end(void* data, const char* el) else url = dash->current_adaptationset_->base_url_ + dash->strXMLText_; + dash->current_representation_->base_url_ = url; + if (dash->current_representation_->flags_ & AdaptiveTree::Representation::TEMPLATE) { if (dash->current_representation_->flags_ & From eb9cfb5a1ddafe4a601231998c8d5f1f10d4bd16 Mon Sep 17 00:00:00 2001 From: Glenn Guy Date: Sun, 13 Jun 2021 08:59:17 +1200 Subject: [PATCH 08/11] [HLS] EXT-X-DISCONTINUITY-SEQUENCE support --- src/common/AdaptiveStream.cpp | 4 +- src/common/AdaptiveTree.cpp | 4 +- src/common/AdaptiveTree.h | 5 +- src/main.cpp | 68 ++++++++++++++++--------- src/main.h | 3 +- src/parser/HLSTree.cpp | 94 ++++++++++++++++++++++++++--------- src/parser/HLSTree.h | 2 + 7 files changed, 125 insertions(+), 55 deletions(-) diff --git a/src/common/AdaptiveStream.cpp b/src/common/AdaptiveStream.cpp index 902fcd99a..308613b16 100644 --- a/src/common/AdaptiveStream.cpp +++ b/src/common/AdaptiveStream.cpp @@ -190,7 +190,7 @@ bool AdaptiveStream::start_stream(const uint32_t seg_offset, bool play_timeshift_buffer) { if (!play_timeshift_buffer && !~seg_offset && tree_.has_timeshift_buffer_ && - current_rep_->segments_.data.size() > 1) + current_rep_->segments_.data.size() > 1 && tree_.periods_.size() == 1) { std::int32_t pos; if (tree_.has_timeshift_buffer_ || tree_.available_time_ >= tree_.stream_start_) @@ -400,7 +400,7 @@ bool AdaptiveStream::ensureSegment() ResetSegment(); thread_data_->signal_dl_.notify_one(); } - else if (tree_.HasUpdateThread()) + else if (tree_.HasUpdateThread() && current_period_ == tree_.periods_.back()) { current_rep_->flags_ |= AdaptiveTree::Representation::WAITFORSEGMENT; Log(LOGLEVEL_DEBUG, "Begin WaitForSegment stream %s", current_rep_->id.c_str()); diff --git a/src/common/AdaptiveTree.cpp b/src/common/AdaptiveTree.cpp index 5238bfc86..7a680576c 100644 --- a/src/common/AdaptiveTree.cpp +++ b/src/common/AdaptiveTree.cpp @@ -78,11 +78,11 @@ namespace adaptive delete *bp; } - void AdaptiveTree::FreeSegments(Representation *rep) + void AdaptiveTree::FreeSegments(Period* period, Representation* rep) { for (std::vector::iterator bs(rep->segments_.data.begin()), es(rep->segments_.data.end()); bs != es; ++bs) { - --current_period_->psshSets_[bs->pssh_set_].use_count_; + --period->psshSets_[bs->pssh_set_].use_count_; if (rep->flags_ & Representation::URLSEGMENTS) delete[] bs->url; } diff --git a/src/common/AdaptiveTree.h b/src/common/AdaptiveTree.h index 3ca34dd99..daa555a11 100644 --- a/src/common/AdaptiveTree.h +++ b/src/common/AdaptiveTree.h @@ -399,7 +399,7 @@ class AdaptiveTree std::vector adaptationSets_; std::string base_url_, id_; - uint32_t timescale_ = 1000, startNumber_ = 1; + uint32_t timescale_ = 1000, startNumber_ = 1, sequence_ = 0; uint64_t start_ = 0; uint64_t startPTS_ = 0; uint64_t duration_ = 0; @@ -423,6 +423,7 @@ class AdaptiveTree XML_Parser parser_; uint32_t currentNode_; uint32_t segcount_; + uint32_t initial_sequence_ = ~0UL; uint64_t overallSeconds_, stream_start_, available_time_, base_time_; uint64_t minPresentationOffset; bool has_timeshift_buffer_, has_overall_seconds_; @@ -465,7 +466,7 @@ class AdaptiveTree StreamType type){}; bool has_type(StreamType t); - void FreeSegments(Representation *rep); + void FreeSegments(Period* period, Representation* rep); uint32_t estimate_segcount(uint64_t duration, uint32_t timescale); double get_download_speed() const { return download_speed_; }; double get_average_download_speed() const { return average_download_speed_; }; diff --git a/src/main.cpp b/src/main.cpp index 9b1c6006e..b7a1e9886 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1036,8 +1036,8 @@ class SampleReader virtual const AP4_Byte* GetSampleData() const = 0; virtual uint64_t GetDuration() const = 0; virtual bool IsEncrypted() const = 0; - virtual void AddStreamType(INPUTSTREAM_INFO::STREAM_TYPE type, uint16_t sid){}; - virtual void SetStreamType(INPUTSTREAM_INFO::STREAM_TYPE type, uint16_t sid){}; + virtual void AddStreamType(INPUTSTREAM_INFO::STREAM_TYPE type, uint32_t sid){}; + virtual void SetStreamType(INPUTSTREAM_INFO::STREAM_TYPE type, uint32_t sid){}; virtual bool RemoveStreamType(INPUTSTREAM_INFO::STREAM_TYPE type) { return true; }; }; @@ -1066,8 +1066,8 @@ class DummyReader : public SampleReader const AP4_Byte* GetSampleData() const override { return nullptr; } uint64_t GetDuration() const override { return 0; } bool IsEncrypted() const override { return false; } - void AddStreamType(INPUTSTREAM_INFO::STREAM_TYPE type, uint16_t sid) override{}; - void SetStreamType(INPUTSTREAM_INFO::STREAM_TYPE type, uint16_t sid) override{}; + void AddStreamType(INPUTSTREAM_INFO::STREAM_TYPE type, uint32_t sid) override{}; + void SetStreamType(INPUTSTREAM_INFO::STREAM_TYPE type, uint32_t sid) override{}; bool RemoveStreamType(INPUTSTREAM_INFO::STREAM_TYPE type) override { return true; }; } DummyReader; @@ -1668,7 +1668,7 @@ class TSSampleReader : public SampleReader, public TSReader m_typeMap[type] = m_typeMap[INPUTSTREAM_INFO::TYPE_NONE] = streamId; }; - void AddStreamType(INPUTSTREAM_INFO::STREAM_TYPE type, uint16_t sid) override + void AddStreamType(INPUTSTREAM_INFO::STREAM_TYPE type, uint32_t sid) override { m_typeMap[type] = sid; m_typeMask |= (1 << type); @@ -1676,7 +1676,7 @@ class TSSampleReader : public SampleReader, public TSReader StartStreaming(m_typeMask); }; - void SetStreamType(INPUTSTREAM_INFO::STREAM_TYPE type, uint16_t sid) override + void SetStreamType(INPUTSTREAM_INFO::STREAM_TYPE type, uint32_t sid) override { m_typeMap[type] = sid; m_typeMask = (1 << type); @@ -1763,7 +1763,7 @@ class TSSampleReader : public SampleReader, public TSReader private: uint32_t m_typeMask; //Bit representation of INPUTSTREAM_INFO::STREAM_TYPES - uint16_t m_typeMap[16]; + uint32_t m_typeMap[16]; bool m_eos = false; bool m_started = false; @@ -2509,12 +2509,7 @@ bool Session::InitializePeriod() adaptiveTree_->next_period_ = nullptr; } - chapter_start_time_ = 0; - for (adaptive::AdaptiveTree::Period* p : adaptiveTree_->periods_) - if (p == adaptiveTree_->current_period_) - break; - else - chapter_start_time_ += (p->duration_ * DVD_TIME_BASE) / p->timescale_; + chapter_start_time_ = GetChapterStartTime(); if (adaptiveTree_->current_period_->encryptionState_ == adaptive::AdaptiveTree::ENCRYTIONSTATE_ENCRYPTED) @@ -2877,7 +2872,7 @@ SampleReader* Session::GetNextSample() if (res->reader_->GetInformation(res->info_)) changed_ = true; if (res->reader_->PTS() != DVD_NOPTS_VALUE) - elapsed_time_ = PTSToElapsed(res->reader_->PTS()) + chapter_start_time_; + elapsed_time_ = PTSToElapsed(res->reader_->PTS()) + GetChapterStartTime(); return res->reader_; } else if (waiting) @@ -3136,6 +3131,29 @@ int64_t Session::GetChapterPos(int ch) const return sum / DVD_TIME_BASE; } +uint64_t Session::GetChapterStartTime() const +{ + uint64_t start_time = 0; + for (adaptive::AdaptiveTree::Period* p : adaptiveTree_->periods_) + if (p == adaptiveTree_->current_period_) + break; + else + start_time += (p->duration_ * DVD_TIME_BASE) / p->timescale_; + return start_time; +} + +int Session::GetPeriodId() const +{ + if (adaptiveTree_) + if (IsLive()) + return adaptiveTree_->current_period_->sequence_ == adaptiveTree_->initial_sequence_ + ? 1 + : adaptiveTree_->current_period_->sequence_ + 1; + else + return GetChapter(); + return -1; +} + bool Session::SeekChapter(int ch) { if (adaptiveTree_->next_period_) @@ -3232,7 +3250,7 @@ class CInputStreamAdaptive : public kodi::addon::CInstanceInputStream private: std::shared_ptr m_session; int m_width, m_height; - uint16_t m_IncludedStreams[16]; + uint32_t m_IncludedStreams[16]; bool m_checkChapterSeek = false; bool m_playTimeshiftBuffer = false; int m_failedSeekTime = ~0; @@ -3414,7 +3432,7 @@ struct INPUTSTREAM_IDS CInputStreamAdaptive::GetStreamIds() if (m_session) { - int chapter = m_session->GetChapter(); + int period_id = m_session->GetPeriodId(); iids.m_streamCount = 0; for (unsigned int i(1); @@ -3433,7 +3451,10 @@ struct INPUTSTREAM_IDS CInputStreamAdaptive::GetStreamIds() if (rep->flags_ & adaptive::AdaptiveTree::Representation::INCLUDEDSTREAM) continue; } - iids.m_streamIds[iids.m_streamCount++] = i + chapter * 1000; + iids.m_streamIds[iids.m_streamCount++] = + m_session->IsLive() + ? i + (m_session->GetStream(i)->stream_.getPeriod()->sequence_ + 1) * 1000 + : i + period_id * 1000; } } } @@ -3484,7 +3505,7 @@ struct INPUTSTREAM_INFO CInputStreamAdaptive::GetStream(int streamid) kodi::Log(ADDON_LOG_DEBUG, "GetStream(%d)", streamid); - Session::STREAM* stream(m_session->GetStream(streamid - m_session->GetChapter() * 1000)); + Session::STREAM* stream(m_session->GetStream(streamid - m_session->GetPeriodId() * 1000)); if (stream) { @@ -3521,7 +3542,7 @@ void CInputStreamAdaptive::EnableStream(int streamid, bool enable) if (!m_session) return; - Session::STREAM* stream(m_session->GetStream(streamid - m_session->GetChapter() * 1000)); + Session::STREAM* stream(m_session->GetStream(streamid - m_session->GetPeriodId() * 1000)); if (!enable && stream && stream->enabled) { @@ -3546,7 +3567,7 @@ bool CInputStreamAdaptive::OpenStream(int streamid) if (!m_session) return false; - Session::STREAM* stream(m_session->GetStream(streamid - m_session->GetChapter() * 1000)); + Session::STREAM* stream(m_session->GetStream(streamid - m_session->GetPeriodId() * 1000)); if (!stream || stream->enabled) return false; @@ -3688,7 +3709,7 @@ bool CInputStreamAdaptive::OpenStream(int streamid) stream->reader_->AddStreamType(static_cast(i), m_IncludedStreams[i]); stream->reader_->GetInformation( - m_session->GetStream(m_IncludedStreams[i] - m_session->GetChapter() * 1000)->info_); + m_session->GetStream(m_IncludedStreams[i] - m_session->GetPeriodId() * 1000)->info_); } } m_session->EnableStream(stream, true); @@ -3770,13 +3791,12 @@ DemuxPacket* CInputStreamAdaptive::DemuxRead(void) return p; } - int currentChapter = m_session->GetChapter(); - if (m_session->SeekChapter(currentChapter + 1)) + if (m_session->SeekChapter(m_session->GetChapter() + 1)) { m_checkChapterSeek = true; for (unsigned int i(1); i <= INPUTSTREAM_IDS::MAX_STREAM_COUNT && i <= m_session->GetStreamCount(); ++i) - EnableStream(i + currentChapter * 1000, false); + EnableStream(i + m_session->GetPeriodId() * 1000, false); m_session->InitializePeriod(); DemuxPacket* p = AllocateDemuxPacket(0); p->iStreamId = DMX_SPECIALID_STREAMCHANGE; diff --git a/src/main.h b/src/main.h index bdc20b1d4..3d9916369 100644 --- a/src/main.h +++ b/src/main.h @@ -163,8 +163,9 @@ class Session: public adaptive::AdaptiveStreamObserver int GetChapterCount() const; const char* GetChapterName(int ch) const; int64_t GetChapterPos(int ch) const; + int GetPeriodId() const; bool SeekChapter(int ch); - uint64_t GetChapterStartTime() { return chapter_start_time_; }; + uint64_t GetChapterStartTime() const; double GetChapterSeekTime() { return chapter_seek_time_; }; void ResetChapterSeekTime() { chapter_seek_time_ = 0; }; diff --git a/src/parser/HLSTree.cpp b/src/parser/HLSTree.cpp index 37d5cef04..f4443c6ef 100644 --- a/src/parser/HLSTree.cpp +++ b/src/parser/HLSTree.cpp @@ -155,8 +155,8 @@ int HLSTree::processEncryption(std::string baseUrl, std::maprepresentations_.begin(), adp->representations_.end(), rep) - adp->representations_.begin(); uint32_t discont_count = 0; + bool cp_lost(false); + Representation* entry_rep = rep; PREPARE_RESULT retVal = PREPARE_RESULT_OK; if (rep->flags_ & Representation::DOWNLOADED) @@ -546,14 +548,47 @@ HLSTree::PREPARE_RESULT HLSTree::prepareRepresentation(Period* period, if (newInterval < updateInterval_) updateInterval_ = newInterval; } + else if (line.compare(0, 30, "#EXT-X-DISCONTINUITY-SEQUENCE:") == 0) + { + m_discontSeq = atol(line.c_str() + 30); + if (!~initial_sequence_) + initial_sequence_ = m_discontSeq; + m_hasDiscontSeq = true; + // make sure first period has a sequence on initial prepare + if (!update && m_discontSeq && !periods_.back()->sequence_) + periods_[0]->sequence_ = m_discontSeq; + + auto bp = periods_.begin(); + while (bp != periods_.end()) + { + if ((*bp)->sequence_ < m_discontSeq) + if (*bp != current_period_) + { + delete *bp; + *bp = nullptr; + bp = periods_.erase(bp); + } + // we end up here after pausing for some time + // remove from periods_ for now and reattach later + else + { + cp_lost = true; + bp = periods_.erase(bp); + } + else + bp++; + } + period = periods_[0]; + adp = period->adaptationSets_[adp_pos]; + rep = adp->representations_[rep_pos]; + } else if (line.compare(0, 21, "#EXT-X-DISCONTINUITY") == 0) { - if (newSegments.size() == 0) - continue; - period->duration_ = pts - newSegments[0]->startPTS_; + period->sequence_ = m_discontSeq + discont_count; + period->duration_ = newSegments.size() ? pts - newSegments[0]->startPTS_ : 0; if (!byteRange) rep->flags_ |= Representation::URLSEGMENTS; - if (rep->containerType_ == CONTAINERTYPE_MP4 && byteRange && + if (rep->containerType_ == CONTAINERTYPE_MP4 && byteRange && newSegments.size() && newSegments.data[0].range_begin_ > 0) { rep->flags_ |= Representation::INITIALIZATION; @@ -561,7 +596,7 @@ HLSTree::PREPARE_RESULT HLSTree::prepareRepresentation(Period* period, rep->initialization_.range_end_ = newSegments.data[0].range_begin_ - 1; rep->initialization_.pssh_set_ = 0; } - FreeSegments(rep); + FreeSegments(period, rep); rep->segments_.swap(newSegments); rep->startNumber_ = newStartNumber; @@ -581,13 +616,13 @@ HLSTree::PREPARE_RESULT HLSTree::prepareRepresentation(Period* period, else period = periods_[discont_count]; + newStartNumber += rep->segments_.data.size(); adp = period->adaptationSets_[adp_pos]; rep = adp->representations_[rep_pos]; segment.range_begin_ = ~0ULL; segment.range_end_ = 0; segment.startPTS_ = ~0ULL; segment.pssh_set_ = 0; - newStartNumber = 0; pts = 0; if (currentEncryptionType == ENCRYPTIONTYPE_WIDEVINE) @@ -669,11 +704,11 @@ HLSTree::PREPARE_RESULT HLSTree::prepareRepresentation(Period* period, rep->initialization_.pssh_set_ = 0; } - FreeSegments(rep); + FreeSegments(period, rep); if (newSegments.data.empty()) { - FreeSegments(rep); + FreeSegments(period, rep); rep->flags_ = 0; return PREPARE_RESULT_FAILURE; } @@ -686,9 +721,11 @@ HLSTree::PREPARE_RESULT HLSTree::prepareRepresentation(Period* period, rep->duration_ = rep->segments_[0] ? (pts - rep->segments_[0]->startPTS_) : 0; - if (discont_count) + period->sequence_ = m_discontSeq + discont_count; + if (discont_count || m_hasDiscontSeq) { - periods_[discont_count]->duration_ = (rep->duration_ * periods_[discont_count]->timescale_) / rep->timescale_; + periods_[discont_count]->duration_ = + (rep->duration_ * periods_[discont_count]->timescale_) / rep->timescale_; overallSeconds_ = 0; for (auto p : periods_) { @@ -708,7 +745,8 @@ HLSTree::PREPARE_RESULT HLSTree::prepareRepresentation(Period* period, if (update) { - if (!segmentId || segmentId < rep->startNumber_) + rep = entry_rep; + if (!segmentId || segmentId < rep->startNumber_ || !~segmentId) rep->current_segment_ = nullptr; else { @@ -717,12 +755,17 @@ HLSTree::PREPARE_RESULT HLSTree::prepareRepresentation(Period* period, rep->current_segment_ = rep->get_segment(segmentId - rep->startNumber_); } if ((rep->flags_ & Representation::WAITFORSEGMENT) && - rep->get_next_segment(rep->current_segment_)) + (rep->get_next_segment(rep->current_segment_) || current_period_ != periods_.back())) rep->flags_ &= ~Representation::WAITFORSEGMENT; } else StartUpdateThread(); + if (cp_lost) + periods_.insert(periods_.begin(), current_period_); + period = current_period_; + adp = period->adaptationSets_[adp_pos]; + rep = adp->representations_[rep_pos]; return retVal; } return PREPARE_RESULT_FAILURE; @@ -824,6 +867,8 @@ void HLSTree::RefreshSegments(Period* period, { if (m_refreshPlayList) { + if (rep->flags_ & Representation::INCLUDEDSTREAM) + return; RefreshUpdateThread(); prepareRepresentation(period, adp, rep, true); } @@ -834,15 +879,16 @@ void HLSTree::RefreshLiveSegments() { if (m_refreshPlayList) { - for (std::vector::const_iterator bp(periods_.begin()), ep(periods_.end()); bp != ep; - ++bp) - for (std::vector::const_iterator ba((*bp)->adaptationSets_.begin()), - ea((*bp)->adaptationSets_.end()); - ba != ea; ++ba) - for (std::vector::iterator br((*ba)->representations_.begin()), - er((*ba)->representations_.end()); - br != er; ++br) - if ((*br)->flags_ & Representation::ENABLED) - prepareRepresentation((*bp), (*ba), (*br), true); + std::vector> refresh_list; + for (std::vector::const_iterator ba(current_period_->adaptationSets_.begin()), + ea(current_period_->adaptationSets_.end()); + ba != ea; ++ba) + for (std::vector::iterator br((*ba)->representations_.begin()), + er((*ba)->representations_.end()); + br != er; ++br) + if ((*br)->flags_ & Representation::ENABLED) + refresh_list.push_back(std::make_tuple(*ba, *br)); + for (auto t : refresh_list) + prepareRepresentation(current_period_, std::get<0>(t), std::get<1>(t), true); } } diff --git a/src/parser/HLSTree.h b/src/parser/HLSTree.h index 3bb5618e2..ebe990564 100644 --- a/src/parser/HLSTree.h +++ b/src/parser/HLSTree.h @@ -82,6 +82,8 @@ namespace adaptive uint8_t m_segmentIntervalSec = 4; AESDecrypter *m_decrypter; std::stringstream manifest_stream; + bool m_hasDiscontSeq = false; + uint32_t m_discontSeq = 0; }; } // namespace From 59e66aad837a07ec87ad41994121a37ce8747d55 Mon Sep 17 00:00:00 2001 From: Glenn Guy Date: Sun, 13 Jun 2021 09:12:41 +1200 Subject: [PATCH 09/11] fix seeking into separate chapters/resume time --- src/main.cpp | 51 ++++++++++++++++++++++++++++++++++++++++++++++++--- src/main.h | 2 ++ 2 files changed, 50 insertions(+), 3 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index b7a1e9886..97d85551b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1039,6 +1039,7 @@ class SampleReader virtual void AddStreamType(INPUTSTREAM_INFO::STREAM_TYPE type, uint32_t sid){}; virtual void SetStreamType(INPUTSTREAM_INFO::STREAM_TYPE type, uint32_t sid){}; virtual bool RemoveStreamType(INPUTSTREAM_INFO::STREAM_TYPE type) { return true; }; + virtual bool IsStarted() const = 0; }; /******************************************************* @@ -1069,6 +1070,7 @@ class DummyReader : public SampleReader void AddStreamType(INPUTSTREAM_INFO::STREAM_TYPE type, uint32_t sid) override{}; void SetStreamType(INPUTSTREAM_INFO::STREAM_TYPE type, uint32_t sid) override{}; bool RemoveStreamType(INPUTSTREAM_INFO::STREAM_TYPE type) override { return true; }; + bool IsStarted() const override { return true; } } DummyReader; /******************************************************* @@ -1247,6 +1249,7 @@ class FragmentedSampleReader : public SampleReader, public AP4_LinearReader } bool EOS() const override { return m_eos; }; + bool IsStarted() const override { return m_started; }; uint64_t DTS() const override { return m_dts; }; uint64_t PTS() const override { return m_pts; }; AP4_UI32 GetStreamId() const override { return m_streamId; }; @@ -1572,6 +1575,7 @@ class SubtitleSampleReader : public SampleReader m_codecHandler = new TTMLCodecHandler(nullptr); } + bool IsStarted() const override { return true; }; bool EOS() const override { return m_eos; }; uint64_t DTS() const override { return m_pts; }; uint64_t PTS() const override { return m_pts; }; @@ -1689,6 +1693,7 @@ class TSSampleReader : public SampleReader, public TSReader return m_typeMask == 0; }; + bool IsStarted() const override { return m_started; } bool EOS() const override { return m_eos; } uint64_t DTS() const override { return m_dts; } uint64_t PTS() const override { return m_pts; } @@ -1783,6 +1788,7 @@ class ADTSSampleReader : public SampleReader, public ADTSReader ADTSSampleReader(AP4_ByteStream* input, AP4_UI32 streamId) : ADTSReader(input), m_streamId(streamId), m_stream(dynamic_cast(input)){}; + bool IsStarted() const override { return m_started; } bool EOS() const override { return m_eos; } uint64_t DTS() const override { return m_pts; } uint64_t PTS() const override { return m_pts; } @@ -1865,6 +1871,7 @@ class WebmSampleReader : public SampleReader, public WebmReader WebmSampleReader(AP4_ByteStream* input, AP4_UI32 streamId) : WebmReader(input), m_streamId(streamId), m_stream(dynamic_cast(input)){}; + bool IsStarted() const override { return m_started; } bool EOS() const override { return m_eos; } uint64_t DTS() const override { return m_dts; } uint64_t PTS() const override { return m_pts; } @@ -2851,6 +2858,25 @@ uint64_t Session::GetTimeshiftBufferStart() return 0ULL; } +void Session::StartReader( + STREAM* stream, uint64_t seekTimeCorrected, int64_t ptsDiff, bool preceeding, bool timing) +{ + bool bReset = true; + if (timing) + seekTimeCorrected += stream->stream_.GetAbsolutePTSOffset(); + else + seekTimeCorrected -= ptsDiff; + stream->stream_.seek_time( + static_cast(seekTimeCorrected / DVD_TIME_BASE), + preceeding, bReset); + if (bReset) + stream->reader_->Reset(false); + bool bStarted = false; + stream->reader_->Start(bStarted); + if (bStarted && (stream->reader_->GetInformation(stream->info_))) + changed_ = true; +} + SampleReader* Session::GetNextSample() { STREAM *res(0), *waiting(0); @@ -2916,6 +2942,7 @@ bool Session::SeekTime(double seekTime, unsigned int streamId, bool preceeding) seekTime -= chapterTime; + // don't try to seek past the end of the stream, leave a sensible amount so we can buffer properly if (adaptiveTree_->has_timeshift_buffer_) { uint64_t curTime, maxTime(0); @@ -2929,11 +2956,19 @@ bool Session::SeekTime(double seekTime, unsigned int streamId, bool preceeding) } } + // correct for starting segment pts value of chapter and chapter offset within program uint64_t seekTimeCorrected = static_cast(seekTime * DVD_TIME_BASE); + int64_t ptsDiff = 0; if (timing_stream_) { + // after seeking across chapters with fmp4 streams the reader will not have started + // so we start here to ensure that we have the required information to correctly + // seek with proper stream alignment + if (!timing_stream_->reader_->IsStarted()) + StartReader(timing_stream_, seekTimeCorrected, ptsDiff, preceeding, true); + seekTimeCorrected += timing_stream_->stream_.GetAbsolutePTSOffset(); - int64_t ptsDiff = timing_stream_->reader_->GetPTSDiff(); + ptsDiff = timing_stream_->reader_->GetPTSDiff(); if (ptsDiff < 0 && seekTimeCorrected + ptsDiff > seekTimeCorrected) seekTimeCorrected = 0; else @@ -2943,13 +2978,19 @@ bool Session::SeekTime(double seekTime, unsigned int streamId, bool preceeding) for (std::vector::const_iterator b(streams_.begin()), e(streams_.end()); b != e; ++b) if ((*b)->enabled && (*b)->reader_ && (streamId == 0 || (*b)->info_.m_pID == streamId)) { - bool bReset; + bool bReset = true; + // all streams must be started before seeking to ensure cross chapter seeks + // will seek to the correct location/segment + if (!(*b)->reader_->IsStarted()) + StartReader((*b), seekTimeCorrected, ptsDiff, preceeding, false); + // advance adaptiveStream to the correct segment (triggers segment download) if ((*b)->stream_.seek_time( static_cast(seekTimeCorrected - (*b)->reader_->GetPTSDiff()) / DVD_TIME_BASE, preceeding, bReset)) { if (bReset) (*b)->reader_->Reset(false); + // advance reader to requested time if (!(*b)->reader_->TimeSeek(seekTimeCorrected, preceeding)) (*b)->reader_->Reset(true); else @@ -2959,7 +3000,11 @@ bool Session::SeekTime(double seekTime, unsigned int streamId, bool preceeding) "seekTime(%0.1lf) for Stream:%d continues at %0.1lf (PTS: %llu)", seekTime, (*b)->info_.m_pID, destTime, (*b)->reader_->PTS()); if ((*b)->info_.m_streamType == INPUTSTREAM_INFO::TYPE_VIDEO) - seekTime = destTime, seekTimeCorrected = (*b)->reader_->PTS(), preceeding = false; + { + seekTime = destTime; + seekTimeCorrected = (*b)->reader_->PTS(); + preceeding = false; + } ret = true; } } diff --git a/src/main.h b/src/main.h index 3d9916369..b4ccf2ac0 100644 --- a/src/main.h +++ b/src/main.h @@ -150,6 +150,8 @@ class Session: public adaptive::AdaptiveStreamObserver uint64_t GetElapsedTimeMs()const { return elapsed_time_ / 1000; }; uint64_t PTSToElapsed(uint64_t pts); uint64_t GetTimeshiftBufferStart(); + void StartReader( + STREAM* stream, uint64_t seekTimeCorrected, int64_t ptsDiff, bool preceeding, bool timing); bool CheckChange(bool bSet = false){ bool ret = changed_; changed_ = bSet; return ret; }; void SetVideoResolution(unsigned int w, unsigned int h) { width_ = w; height_ = h;}; bool SeekTime(double seekTime, unsigned int streamId = 0, bool preceeding=true); From 8a21fd2a8f7d69e2cf999c7b37895d33e72cce10 Mon Sep 17 00:00:00 2001 From: Portisch Date: Tue, 6 Jul 2021 15:23:43 +0200 Subject: [PATCH 10/11] Fix kodi freeze by session close --- wvdecrypter/cdm/media/cdm/cdm_adapter.cc | 15 ++++++++--- wvdecrypter/cdm/media/cdm/cdm_adapter.h | 5 ++++ wvdecrypter/wvdecrypter.cpp | 32 +++++++++++++++++++++--- 3 files changed, 45 insertions(+), 7 deletions(-) diff --git a/wvdecrypter/cdm/media/cdm/cdm_adapter.cc b/wvdecrypter/cdm/media/cdm/cdm_adapter.cc index 873a4afd2..f5b37e2c5 100644 --- a/wvdecrypter/cdm/media/cdm/cdm_adapter.cc +++ b/wvdecrypter/cdm/media/cdm/cdm_adapter.cc @@ -5,7 +5,6 @@ #include "cdm_adapter.h" #include #include -#include #define DCHECK(condition) assert(condition) @@ -135,6 +134,7 @@ CdmAdapter::CdmAdapter( , cdm_config_(cdm_config) , active_buffer_(0) , cdm9_(0), cdm10_(0), cdm11_(0) +, session_active_(false) { //DCHECK(!key_system_.empty()); Initialize(); @@ -324,10 +324,16 @@ void CdmAdapter::UpdateSession(uint32_t promise_id, response, response_size); } +void CdmAdapter::SetSessionActive() +{ + session_active_ = true; +} + void CdmAdapter::CloseSession(uint32_t promise_id, const char* session_id, uint32_t session_id_size) { + session_active_ = false; exit_thread_flag = true; while (timer_thread_running) { @@ -502,8 +508,11 @@ cdm::Buffer* CdmAdapter::Allocate(uint32_t capacity) void CdmAdapter::SetTimer(int64_t delay_ms, void* context) { //LICENSERENEWAL - exit_thread_flag = false; - std::thread(timerfunc, shared_from_this(), delay_ms, context).detach(); + if (session_active_) + { + exit_thread_flag = false; + std::thread(timerfunc, shared_from_this(), delay_ms, context).detach(); + } } cdm::Time CdmAdapter::GetCurrentWallTime() diff --git a/wvdecrypter/cdm/media/cdm/cdm_adapter.h b/wvdecrypter/cdm/media/cdm/cdm_adapter.h index d722f6cf0..89afef688 100644 --- a/wvdecrypter/cdm/media/cdm/cdm_adapter.h +++ b/wvdecrypter/cdm/media/cdm/cdm_adapter.h @@ -6,6 +6,7 @@ #include #include #include +#include #include "../../base/native_library.h" #include "../../base/compiler_specific.h" @@ -100,6 +101,8 @@ class CdmAdapter : public std::enable_shared_from_this const uint8_t* response, uint32_t response_size); + void SetSessionActive(); + void CloseSession(uint32_t promise_id, const char* session_id, uint32_t session_id_size); @@ -238,6 +241,8 @@ class CdmAdapter : public std::enable_shared_from_this cdm::ContentDecryptionModule_10 *cdm10_; cdm::ContentDecryptionModule_11 *cdm11_; + std::atomic session_active_; + DISALLOW_COPY_AND_ASSIGN(CdmAdapter); }; diff --git a/wvdecrypter/wvdecrypter.cpp b/wvdecrypter/wvdecrypter.cpp index 17fcfe5e3..4e8b079ef 100644 --- a/wvdecrypter/wvdecrypter.cpp +++ b/wvdecrypter/wvdecrypter.cpp @@ -174,12 +174,15 @@ class WV_CencSingleSampleDecrypter : public AP4_CencSingleSampleDecrypter void GetCapabilities(const uint8_t* key, uint32_t media, SSD_DECRYPTER::SSD_CAPS &caps); virtual const char *GetSessionId() override; + void SetSessionActive(); + void CloseSessionId(); void SetSession(const char* session, uint32_t session_size, const uint8_t *data, size_t data_size) { std::lock_guard lock(renewal_lock_); session_ = std::string(session, session_size); challenge_.SetData(data, data_size); + Log(SSD_HOST::LL_DEBUG, "%s: opened session with Id: %s", __func__, session_.c_str()); } void AddSessionKey(const uint8_t *data, size_t data_size, uint32_t status); @@ -388,7 +391,10 @@ void WV_DRM::OnCDMMessage(const char* session, uint32_t session_size, CDMADPMSG return; if (msg == CDMADPMSG::kSessionMessage) + { (*b)->SetSession(session, session_size, data, data_size); + (*b)->SetSessionActive(); + } else if (msg == CDMADPMSG::kSessionKeysChange) (*b)->AddSessionKey(data, data_size, status); }; @@ -477,8 +483,7 @@ WV_CencSingleSampleDecrypter::WV_CencSingleSampleDecrypter(WV_DRM &drm, AP4_Data if (keys_.empty()) { Log(SSD_HOST::LL_ERROR, "License update not successful (no keys)"); - drm_.GetCdmAdapter()->CloseSession(++promise_id_, session_.data(), session_.size()); - session_.clear(); + CloseSessionId(); return; } Log(SSD_HOST::LL_DEBUG, "License update successful"); @@ -486,8 +491,6 @@ WV_CencSingleSampleDecrypter::WV_CencSingleSampleDecrypter(WV_DRM &drm, AP4_Data WV_CencSingleSampleDecrypter::~WV_CencSingleSampleDecrypter() { - if (!session_.empty()) - drm_.GetCdmAdapter()->CloseSession(++promise_id_, session_.data(), session_.size()); drm_.removessd(this); free(subsample_buffer_decrypt_); free(subsample_buffer_video_); @@ -573,6 +576,23 @@ const char *WV_CencSingleSampleDecrypter::GetSessionId() return session_.empty()? nullptr : session_.c_str(); } +void WV_CencSingleSampleDecrypter::SetSessionActive() +{ + drm_.GetCdmAdapter()->SetSessionActive(); +} + +void WV_CencSingleSampleDecrypter::CloseSessionId() +{ + if (!session_.empty()) + { + Log(SSD_HOST::LL_DEBUG, "%s: close session with Id: %s", __func__, session_.c_str()); + drm_.GetCdmAdapter()->CloseSession(++promise_id_, session_.data(), session_.size()); + session_.clear(); + + Log(SSD_HOST::LL_DEBUG, "%s: session closed", __func__); + } +} + void WV_CencSingleSampleDecrypter::CheckLicenseRenewal() { { @@ -1418,7 +1438,11 @@ class WVDecrypter : public SSD_DECRYPTER virtual void DestroySingleSampleDecrypter(AP4_CencSingleSampleDecrypter* decrypter) override { if (decrypter) + { + // close session before dispose + static_cast(decrypter)->CloseSessionId(); delete static_cast(decrypter); + } } virtual void GetCapabilities(AP4_CencSingleSampleDecrypter* decrypter, const uint8_t *keyid, uint32_t media, SSD_DECRYPTER::SSD_CAPS &caps) override From d013940ab74f0610849ffb0f60cb374aa4b11688 Mon Sep 17 00:00:00 2001 From: Matthew Huisman Date: Sun, 13 Jun 2021 09:34:12 +1200 Subject: [PATCH 11/11] bump to 2.4.8 --- README.md | 16 ++++++---------- inputstream.adaptive/addon.xml.in | 14 +++++++++++++- inputstream.adaptive/changelog.txt | 18 ------------------ 3 files changed, 19 insertions(+), 29 deletions(-) delete mode 100644 inputstream.adaptive/changelog.txt diff --git a/README.md b/README.md index d679d6fae..0d91a7362 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# inputstream.adaptive (2.4.7) +# inputstream.adaptive (2.4.8) This is an adaptive file addon for kodi's new InputStream Interface. @@ -38,17 +38,13 @@ If your display resolution is 720p, you will not be able to watch 1080p videos i ##### TODO's: - Adaptive bitrate switching is prepared but currently not yet activated - Automatic / fixed video stream selection depending on max. visible display rect (some work has to be done at the inputstream interface). -- DASH implementation of periods (currently only the first period is considered) -- There will be many dash mpd, smoothstream or hls manifest types currently not supported - must be extended. +- There will be many dash mpd, smoothstream or hls manifest types currently not supported - must be extended. ##### Notes: - This addon uses threads to download segments. The memory consumption is the sum of single segment from each stream currently playing. Refering to known streams it is < 10MB for 720p videos. ##### Credits: -[@fernetmenta](github.com/fernetmenta) Best support I ever got regarding streams / codecs and kodi internals. -[@notspiff](https://github.com/notspiff) Thanks for your ideas / tipps regarding kodi file system -[bento4 library](https://www.bento4.com/) For me the best library choice for mp4 streams. Well written and extensible! - -##### Continuous integration: -[Travis CI build state:](https://travis-ci.org/peak3d) ![alt tag](https://travis-ci.org/peak3d/inputstream.adaptive.svg?branch=master) -[Appveyor:](https://ci.appveyor.com/project/peak3d) ![alt tag](https://ci.appveyor.com/api/projects/status/ah9s8usgxhangq7o?svg=true) +[@peak3d](https://github.com/peak3d) Original author / creator. Superstar! +[@fernetmenta](https://github.com/fernetmenta) Best support regarding streams / codecs and kodi internals. +[@notspiff](https://github.com/notspiff) Ideas / tips regarding kodi file system. +[bento4 library](https://www.bento4.com/) Great library for mp4 streams. Well written and extensible! diff --git a/inputstream.adaptive/addon.xml.in b/inputstream.adaptive/addon.xml.in index 105258542..b7ef7bba0 100644 --- a/inputstream.adaptive/addon.xml.in +++ b/inputstream.adaptive/addon.xml.in @@ -1,7 +1,7 @@ @ADDON_DEPENDS@ @@ -19,6 +19,18 @@ Cliente InputStream para flujo de datos adaptativos @PLATFORM@ +v2.4.8 (2021-07-11) +- [DASH] time and number placeholders can be used simultaneously +- License renewal for widevine +- [DASH] fix for minimumUpdatePeriod=0 +- [HLS] support webvtt subtitle extensions +- [DASH] manifest_update_parameter property defaults to 'full' for SegmentTimeline +- Fix decode failures on some Widevine content +- [DASH] fix segmentTemplate calculation +- [HLS] EXT-X-DISCONTINUITY-SEQUENCE support +- Fix seeking into separate chapters/resume time +- Fix kodi freeze by session close + v2.4.7 (2021-04-10) - [Dash] Fix missing audio languages - [Dash] Correctly set timeshift_buffer (live) diff --git a/inputstream.adaptive/changelog.txt b/inputstream.adaptive/changelog.txt deleted file mode 100644 index a1f9e679f..000000000 --- a/inputstream.adaptive/changelog.txt +++ /dev/null @@ -1,18 +0,0 @@ -1.0.8 -- Fix missing brackets when calculating livestream - start -1.0.7 -- ServerCertificate / SSD version bump -> 6 -1.0.6 -- allow some frames to fail decrypting -1.0.5 -- normalize livestream's timestamp values -1.0.4 -- Parse BaseUrl from top mpd level -1.0.3 -- Start livestreams 12 secs before "now" -1.0.2 -- Fix live-streams by using 64-bit for frame-duration transformation -1.0.1 -- bumb to force update -1.0.0 -- initial release