diff --git a/src/projects/config/items/virtual_hosts/applications/publishers/ll_hls_publisher.h b/src/projects/config/items/virtual_hosts/applications/publishers/ll_hls_publisher.h index 7c5bdbda2..6af6260c2 100644 --- a/src/projects/config/items/virtual_hosts/applications/publishers/ll_hls_publisher.h +++ b/src/projects/config/items/virtual_hosts/applications/publishers/ll_hls_publisher.h @@ -37,6 +37,7 @@ namespace cfg LLHlsDvr _dvr; LLHlsDrm _drm; bool _server_time_based_segment_numbering = false; + bool _enable_preload_hint = true; public: PublisherType GetType() const override @@ -54,6 +55,7 @@ namespace cfg CFG_DECLARE_CONST_REF_GETTER_OF(GetCacheControl, _cache_control) CFG_DECLARE_CONST_REF_GETTER_OF(GetDvr, _dvr) CFG_DECLARE_CONST_REF_GETTER_OF(GetDrm, _drm) + CFG_DECLARE_CONST_REF_GETTER_OF(IsPreloadHintEnabled, _enable_preload_hint) protected: void MakeList() override @@ -71,6 +73,7 @@ namespace cfg Register("CacheControl", &_cache_control); Register("DVR", &_dvr); Register("DRM", &_drm); + Register("EnablePreloadHint", &_enable_preload_hint); } }; } // namespace pub diff --git a/src/projects/modules/containers/bmff/fmp4_packager/fmp4_storage.cpp b/src/projects/modules/containers/bmff/fmp4_packager/fmp4_storage.cpp index e32da203a..84c405682 100644 --- a/src/projects/modules/containers/bmff/fmp4_packager/fmp4_storage.cpp +++ b/src/projects/modules/containers/bmff/fmp4_packager/fmp4_storage.cpp @@ -266,54 +266,59 @@ namespace bmff return segment; } - bool FMP4Storage::AppendMediaChunk(const std::shared_ptr &chunk, int64_t start_timestamp, double duration_ms, bool independent, bool last_chunk) + std::shared_ptr FMP4Storage::CreateNextSegment() { - auto segment = GetLastSegment(); - - if (segment == nullptr || segment->IsCompleted()) + // Create next segment + auto segment = std::make_shared(GetLastSegmentNumber() + 1, _config.segment_duration_ms); { - // Notify observer - if (segment != nullptr && _observer != nullptr) - { - _observer->OnMediaSegmentUpdated(_track->GetId(), segment->GetNumber()); - } + std::lock_guard lock(_segments_lock); + _segments.emplace(segment->GetNumber(), segment); - // Create next segment - segment = std::make_shared(GetLastSegmentNumber() + 1, _config.segment_duration_ms); + // Delete old segments + if (_segments.size() > _config.max_segments) { - std::lock_guard lock(_segments_lock); - _segments.emplace(segment->GetNumber(), segment); - - // Delete old segments - if (_segments.size() > _config.max_segments) - { - auto old_it = _segments.begin(); - std::advance(old_it, (_segments.size() - _config.max_segments) - 1); + auto old_it = _segments.begin(); + std::advance(old_it, (_segments.size() - _config.max_segments) - 1); - auto old_segment = old_it->second; + auto old_segment = old_it->second; - // Since the chunklist is updated late, the player may request deleted segments in the meantime, so it actually deletes them a bit late. - if (_segments.size() > _config.max_segments + 3) - { - _segments.erase(_segments.begin()); - } + // Since the chunklist is updated late, the player may request deleted segments in the meantime, so it actually deletes them a bit late. + if (_segments.size() > _config.max_segments + 3) + { + _segments.erase(_segments.begin()); + } - // DVR - if (_config.dvr_enabled) - { - SaveMediaSegmentToFile(old_segment); - } - else + // DVR + if (_config.dvr_enabled) + { + SaveMediaSegmentToFile(old_segment); + } + else + { + if (_observer != nullptr) { - if (_observer != nullptr) - { - _observer->OnMediaSegmentDeleted(_track->GetId(), old_segment->GetNumber()); - } + _observer->OnMediaSegmentDeleted(_track->GetId(), old_segment->GetNumber()); } } } } + if (_observer != nullptr) + { + _observer->OnMediaSegmentCreated(_track->GetId(), segment->GetNumber()); + } + + return segment; + } + + bool FMP4Storage::AppendMediaChunk(const std::shared_ptr &chunk, int64_t start_timestamp, double duration_ms, bool independent, bool last_chunk) + { + auto segment = GetLastSegment(); + if (segment == nullptr || segment->IsCompleted() == true) + { + segment = CreateNextSegment(); + } + if (segment->AppendChunkData(chunk, start_timestamp, duration_ms, independent) == false) { return false; @@ -353,7 +358,8 @@ namespace bmff // Notify observer if (_observer != nullptr) { - _observer->OnMediaChunkUpdated(_track->GetId(), segment->GetNumber(), segment->GetLastChunkNumber()); + bool last_chunk = segment->IsCompleted() == true; + _observer->OnMediaChunkUpdated(_track->GetId(), segment->GetNumber(), segment->GetLastChunkNumber(), last_chunk); } return true; diff --git a/src/projects/modules/containers/bmff/fmp4_packager/fmp4_storage.h b/src/projects/modules/containers/bmff/fmp4_packager/fmp4_storage.h index 5e966119d..67d77474c 100644 --- a/src/projects/modules/containers/bmff/fmp4_packager/fmp4_storage.h +++ b/src/projects/modules/containers/bmff/fmp4_packager/fmp4_storage.h @@ -16,8 +16,8 @@ namespace bmff { public: virtual void OnFMp4StorageInitialized(const int32_t &track_id) = 0; - virtual void OnMediaSegmentUpdated(const int32_t &track_id, const uint32_t &segment_number) = 0; - virtual void OnMediaChunkUpdated(const int32_t &track_id, const uint32_t &segment_number, const uint32_t &chunk_number) = 0; + virtual void OnMediaSegmentCreated(const int32_t &track_id, const uint32_t &segment_number) = 0; + virtual void OnMediaChunkUpdated(const int32_t &track_id, const uint32_t &segment_number, const uint32_t &chunk_number, bool last_chunk) = 0; virtual void OnMediaSegmentDeleted(const int32_t &track_id, const uint32_t &segment_number) = 0; }; @@ -161,6 +161,8 @@ namespace bmff bool SaveMediaSegmentToFile(const std::shared_ptr &segment); std::shared_ptr LoadMediaSegmentFromFile(uint32_t segment_number) const; + std::shared_ptr CreateNextSegment(); + Config _config; std::shared_ptr _track; diff --git a/src/projects/publishers/llhls/llhls_chunklist.cpp b/src/projects/publishers/llhls/llhls_chunklist.cpp index cf5b555e2..f7609eb6e 100644 --- a/src/projects/publishers/llhls/llhls_chunklist.cpp +++ b/src/projects/publishers/llhls/llhls_chunklist.cpp @@ -12,7 +12,9 @@ #include #include -LLHlsChunklist::LLHlsChunklist(const ov::String &url, const std::shared_ptr &track, uint32_t target_duration, double part_target_duration, const ov::String &map_uri) +LLHlsChunklist::LLHlsChunklist(const ov::String &url, const std::shared_ptr &track, + uint32_t target_duration, double part_target_duration, + const ov::String &map_uri, bool preload_hint_enabled) { _url = url; _track = track; @@ -20,6 +22,7 @@ LLHlsChunklist::LLHlsChunklist(const ov::String &url, const std::shared_ptrGetVariantName().CStr()); } @@ -75,70 +78,40 @@ void LLHlsChunklist::SetPartHoldBack(const float &part_hold_back) _part_hold_back = part_hold_back; } -bool LLHlsChunklist::AppendSegmentInfo(const SegmentInfo &info) +bool LLHlsChunklist::CreateSegmentInfo(const SegmentInfo &info) { - logtd("AppendSegmentInfo[Track : %s/%s]: %s", _track->GetPublicName().CStr(), _track->GetVariantName().CStr(), info.ToString().CStr()); - - if (info.GetSequence() < _last_segment_sequence) - { - logtc("The sequence number of the segment to be added is less than the last segment. segment(%lld) last(%lld)", info.GetSequence(), _last_segment_sequence.load()); - return false; - } + logtd("UpdateSegmentInfo[Track : %s/%s]: %s", _track->GetPublicName().CStr(), _track->GetVariantName().CStr(), info.ToString().CStr()); std::shared_ptr segment = GetSegmentInfo(info.GetSequence()); - bool is_new_segment = false; if (segment == nullptr) { - // Sequence must be sequential - if (_last_segment_sequence + 1 != info.GetSequence()) - { - logtc("Sequence is not sequential. last_sequence(%lld) current_sequence(%lld)", _last_segment_sequence.load(), info.GetSequence()); - return false; - } - // Lock std::unique_lock lock(_segments_guard); // Create segment segment = std::make_shared(info); _segments.emplace(segment->GetSequence(), segment); - is_new_segment = true; + _last_segment_sequence = segment->GetSequence(); + _last_partial_segment_sequence = -1; } else { - // Update segment - segment->UpdateInfo(info.GetStartTime(), info.GetDuration(), info.GetSize(), info.GetUrl(), info.IsIndependent()); + logte("CreateSegmentInfo[Track : %s/%s]: segment(%lld) is already exist", + _track->GetPublicName().CStr(), _track->GetVariantName().CStr(), info.GetSequence()); } - segment->SetCompleted(); - UpdateCacheForDefaultChunklist(); - if (is_new_segment) - { - _last_segment_sequence = info.GetSequence(); - } - - _first_segment = false; return true; } bool LLHlsChunklist::AppendPartialSegmentInfo(uint32_t segment_sequence, const SegmentInfo &info) { - if (segment_sequence < _last_segment_sequence) - { - return false; - } - std::shared_ptr segment = GetSegmentInfo(segment_sequence); if (segment == nullptr) { - // Lock - std::unique_lock lock(_segments_guard); - - // Create segment - segment = std::make_shared(segment_sequence); - _segments.emplace(segment_sequence, segment); - _last_segment_sequence = segment_sequence; + logte("AppendPartialSegmentInfo[Track : %s/%s]: segment(%lld) is not found", + _track->GetPublicName().CStr(), _track->GetVariantName().CStr(), segment_sequence); + return false; } // part duration is calculated on first segment @@ -147,10 +120,17 @@ bool LLHlsChunklist::AppendPartialSegmentInfo(uint32_t segment_sequence, const S _max_part_duration = std::max(_max_part_duration, info.GetDuration()); } + if (info.IsCompleted() == true) + { + segment->SetCompleted(); + _first_segment = false; + } + + _last_partial_segment_sequence = info.GetSequence(); + segment->InsertPartialSegmentInfo(std::make_shared(info)); - + UpdateCacheForDefaultChunklist(); - _last_partial_segment_sequence = info.GetSequence(); return true; } @@ -215,6 +195,19 @@ bool LLHlsChunklist::SaveOldSegmentInfo(std::shared_ptr &segment_in return true; } +std::shared_ptr LLHlsChunklist::GetLastSegmentInfo() const +{ + // lock + std::shared_lock lock(_segments_guard); + + if (_segments.empty()) + { + return nullptr; + } + + return _segments.rbegin()->second; +} + std::shared_ptr LLHlsChunklist::GetSegmentInfo(uint32_t segment_sequence) const { // lock @@ -297,7 +290,7 @@ ov::String LLHlsChunklist::MakeChunklist(const ov::String &query_string, bool sk playlist.AppendFormat("#EXTM3U\n"); - playlist.AppendFormat("#EXT-X-VERSION:%d\n", 10); + playlist.AppendFormat("#EXT-X-VERSION:%d\n", 9); // Note that in protocol version 6, the semantics of the EXT- // X-TARGETDURATION tag changed slightly. In protocol version 5 and // earlier it indicated the maximum segment duration; in protocol @@ -310,7 +303,7 @@ ov::String LLHlsChunklist::MakeChunklist(const ov::String &query_string, bool sk { // X-SERVER-CONTROL playlist.AppendFormat("#EXT-X-SERVER-CONTROL:CAN-BLOCK-RELOAD=YES,PART-HOLD-BACK=%f\n", _part_hold_back); - playlist.AppendFormat("#EXT-X-PART-INF:PART-TARGET=%lf\n", _max_part_duration); + playlist.AppendFormat("#EXT-X-PART-INF:PART-TARGET=%lf\n", _part_target_duration); } auto first_segment = _segments.begin()->second; @@ -359,6 +352,11 @@ ov::String LLHlsChunklist::MakeChunklist(const ov::String &query_string, bool sk continue; } + if (segment->GetPartialSegmentsCount() == 0) + { + continue; + } + std::chrono::system_clock::time_point tp{std::chrono::milliseconds{segment->GetStartTime()}}; playlist.AppendFormat("#EXT-X-PROGRAM-DATE-TIME:%s\n", ov::Converter::ToISO8601String(tp).CStr()); @@ -378,14 +376,16 @@ ov::String LLHlsChunklist::MakeChunklist(const ov::String &query_string, bool sk playlist.AppendFormat("?%s", query_string.CStr()); } playlist.AppendFormat("\""); - if (_track->GetMediaType() == cmn::MediaType::Video && partial_segment->IsIndependent() == true) + if (_track->GetMediaType() == cmn::MediaType::Audio || (_track->GetMediaType() == cmn::MediaType::Video && partial_segment->IsIndependent() == true)) { playlist.AppendFormat(",INDEPENDENT=YES"); } + playlist.Append("\n"); - // If it is the last one, output PRELOAD-HINT - if (segment->GetSequence() == last_segment->GetSequence() && + //If it is the last one, output PRELOAD-HINT + if (_preload_hint_enabled == true && + segment->GetSequence() == last_segment->GetSequence() && partial_segment == segment->GetPartialSegments().back()) { playlist.AppendFormat("#EXT-X-PRELOAD-HINT:TYPE=PART,URI=\"%s", partial_segment->GetNextUrl().CStr()); @@ -399,7 +399,9 @@ ov::String LLHlsChunklist::MakeChunklist(const ov::String &query_string, bool sk } } - if (segment->IsCompleted()) + // Don't print Completed segment info if it is the last segment + // It will be printed when the next segment is created. + if (segment->IsCompleted() && segment->GetSequence() != last_segment->GetSequence()) { playlist.AppendFormat("#EXTINF:%lf,\n", segment->GetDuration()); playlist.AppendFormat("%s", segment->GetUrl().CStr()); @@ -412,6 +414,21 @@ ov::String LLHlsChunklist::MakeChunklist(const ov::String &query_string, bool sk } segment_lock.unlock(); + if (last_segment->GetPartialSegmentsCount() == 0) + { + // preload hints + if (vod == false && legacy == false) + { + playlist.AppendFormat("#EXT-X-PRELOAD-HINT:TYPE=PART,URI=\"%s", last_segment->GetNextUrl().CStr()); + if (query_string.IsEmpty() == false) + { + playlist.AppendFormat("?%s", query_string.CStr()); + } + playlist.AppendFormat("\"\n"); + } + } + +#if 1 if (vod == false) { // Output #EXT-X-RENDITION-REPORT @@ -424,6 +441,11 @@ ov::String LLHlsChunklist::MakeChunklist(const ov::String &query_string, bool sk { continue; } + // Skip another media type + if (rendition->GetTrack()->GetMediaType() != _track->GetMediaType()) + { + continue; + } playlist.AppendFormat("#EXT-X-RENDITION-REPORT:URI=\"%s", rendition->GetUrl().CStr()); if (query_string.IsEmpty() == false) @@ -439,8 +461,7 @@ ov::String LLHlsChunklist::MakeChunklist(const ov::String &query_string, bool sk if (legacy == true && last_msn > 0) { // https://datatracker.ietf.org/doc/html/draft-pantos-hls-rfc8216bis#section-4.4.5.4 - // If the Rendition contains Partial Segments then this value - // is the Media Sequence Number of the last Partial Segment. + // If the Rendition contains Partial Segments then this value is the Media Sequence Number of the last Partial Segment. // In legacy, the completed msn is reported. last_msn -= 1; @@ -456,6 +477,7 @@ ov::String LLHlsChunklist::MakeChunklist(const ov::String &query_string, bool sk playlist.AppendFormat("\n"); } } +#endif if (vod == true) { diff --git a/src/projects/publishers/llhls/llhls_chunklist.h b/src/projects/publishers/llhls/llhls_chunklist.h index b2be60f86..9c6742ad6 100644 --- a/src/projects/publishers/llhls/llhls_chunklist.h +++ b/src/projects/publishers/llhls/llhls_chunklist.h @@ -25,7 +25,21 @@ class LLHlsChunklist { } - SegmentInfo(uint32_t sequence, int64_t start_time, double duration, uint64_t size, ov::String url, ov::String next_url, bool is_independent) + // For Segment + SegmentInfo(uint32_t sequence, ov::String url) + : _sequence(sequence) + , _start_time(0) + , _duration(0) + , _size(0) + , _url(url) + , _next_url("") + , _is_independent(true) + , _completed(false) + { + } + + // For Partial Segment + SegmentInfo(uint32_t sequence, int64_t start_time, double duration, uint64_t size, ov::String url, ov::String next_url, bool is_independent, bool completed) : _sequence(sequence) , _start_time(start_time) , _duration(duration) @@ -33,6 +47,7 @@ class LLHlsChunklist , _url(url) , _next_url(next_url) , _is_independent(is_independent) + , _completed(completed) { } @@ -93,6 +108,14 @@ class LLHlsChunklist return false; } + if (_partial_segments.empty() == true) + { + // first partial segment + _start_time = partial_segment->GetStartTime(); + } + + _duration += partial_segment->GetDuration(); + _partial_segments.push_back(partial_segment); return true; @@ -104,6 +127,12 @@ class LLHlsChunklist return _partial_segments; } + // Get Partial segments count + uint32_t GetPartialSegmentsCount() const + { + return _partial_segments.size(); + } + // Clear partial segments void ClearPartialSegments() { @@ -155,7 +184,9 @@ class LLHlsChunklist std::deque> _partial_segments; }; // class SegmentInfo - LLHlsChunklist(const ov::String &url, const std::shared_ptr &track, uint32_t target_duration, double part_target_duration, const ov::String &map_uri); + LLHlsChunklist(const ov::String &url, const std::shared_ptr &track, + uint32_t target_duration, double part_target_duration, + const ov::String &map_uri, bool preload_hint_enabled); ~LLHlsChunklist(); @@ -177,7 +208,7 @@ class LLHlsChunklist void SetPartHoldBack(const float &part_hold_back); - bool AppendSegmentInfo(const SegmentInfo &info); + bool CreateSegmentInfo(const SegmentInfo &info); bool AppendPartialSegmentInfo(uint32_t segment_sequence, const SegmentInfo &info); bool RemoveSegmentInfo(uint32_t segment_sequence); @@ -188,6 +219,8 @@ class LLHlsChunklist bool GetLastSequenceNumber(int64_t &msn, int64_t &psn) const; private: + std::shared_ptr GetLastSegmentInfo() const; + bool SaveOldSegmentInfo(std::shared_ptr &segment_info); ov::String MakeChunklist(const ov::String &query_string, bool skip, bool legacy, bool vod = false, uint32_t vod_start_segment_number = 0) const; @@ -203,6 +236,7 @@ class LLHlsChunklist double _max_part_duration = 0; double _part_hold_back = 0; ov::String _map_uri; + bool _preload_hint_enabled = true; std::atomic _last_segment_sequence = -1; std::atomic _last_partial_segment_sequence = -1; diff --git a/src/projects/publishers/llhls/llhls_stream.cpp b/src/projects/publishers/llhls/llhls_stream.cpp index a7567898b..4b2f4a46d 100755 --- a/src/projects/publishers/llhls/llhls_stream.cpp +++ b/src/projects/publishers/llhls/llhls_stream.cpp @@ -78,6 +78,7 @@ bool LLHlsStream::Start() _storage_config.server_time_based_segment_numbering = llhls_config.IsServerTimeBasedSegmentNumbering(); _configured_part_hold_back = llhls_config.GetPartHoldBack(); + _preload_hint_enabled = llhls_config.IsPreloadHintEnabled(); // Find data track auto data_track = GetFirstTrackByType(cmn::MediaType::Data); @@ -717,8 +718,13 @@ std::tuple> LLHlsStr if (msn > last_msn || (msn >= last_msn && psn > last_psn)) { // Hold the request until a Playlist contains a Segment with the requested Sequence Number + logtd("Accepted chunklist for track_id = %d, msn = %ld, psn = %ld (last_msn = %ld, last_psn = %ld)", track_id, msn, psn, last_msn, last_psn); return {RequestResult::Accepted, nullptr}; } + else + { + logtd("Get chunklist for track_id = %d, msn = %ld, psn = %ld (last_msn = %ld, last_psn = %ld)", track_id, msn, psn, last_msn, last_psn); + } } if (gzip == true) @@ -773,16 +779,15 @@ std::tuple> LLHlsStream::G auto [last_segment_number, last_chunk_number] = storage->GetLastChunkNumber(); - if (segment_number == last_segment_number && chunk_number > last_chunk_number) + if ((segment_number > last_segment_number) || (segment_number == last_segment_number && chunk_number > last_chunk_number)) { + logtd("Accepted chunk for track_id = %d, segment = %ld, chunk = %ld (last_segment = %ld, last_chunk = %ld)", track_id, segment_number, chunk_number, last_segment_number, last_chunk_number); // Hold the request until a Playlist contains a Segment with the requested Sequence Number return {RequestResult::Accepted, nullptr}; } - else if (segment_number > last_segment_number) + else { - // Not Found - logtw("Could not find segment for track_id = %d, segment = %ld (last_segment = %ld)", track_id, segment_number, last_segment_number); - return {RequestResult::NotFound, nullptr}; + logtd("Get chunk for track_id = %d, segment = %ld, chunk = %ld (last_segment = %ld, last_chunk = %ld)", track_id, segment_number, chunk_number, last_segment_number, last_chunk_number); } auto chunk = storage->GetMediaChunk(segment_number, chunk_number); @@ -995,7 +1000,8 @@ bool LLHlsStream::AddPackager(const std::shared_ptr &media_tra GetTrack(track_id), segment_duration, chunk_duration, - GetInitializationSegmentName(track_id)); + GetInitializationSegmentName(track_id), + _preload_hint_enabled); if (cenc_property.scheme != bmff::CencProtectScheme::None) { @@ -1097,13 +1103,27 @@ ov::String LLHlsStream::GetPartialSegmentName(const int32_t &track_id, const int _stream_key.CStr()); } -ov::String LLHlsStream::GetNextPartialSegmentName(const int32_t &track_id, const int64_t &segment_number, const int64_t &partial_number) const +ov::String LLHlsStream::GetNextPartialSegmentName(const int32_t &track_id, const int64_t &segment_number, const int64_t &partial_number, bool last_chunk) const { + auto next_segment_number = 0; + auto next_partial_number = 0; + + if (last_chunk == true) + { + next_segment_number = segment_number + 1; + next_partial_number = 0; + } + else + { + next_segment_number = segment_number; + next_partial_number = partial_number + 1; + } + // part______llhls.m4s return ov::String::FormatString("part_%d_%lld_%lld_%s_%s_llhls.m4s", track_id, - segment_number, - partial_number + 1, + next_segment_number, + next_partial_number, StringFromMediaType(GetTrack(track_id)->GetMediaType()).LowerCaseString().CStr(), _stream_key.CStr()); } @@ -1164,7 +1184,7 @@ bool LLHlsStream::CheckPlaylistReady() return true; } -void LLHlsStream::OnMediaSegmentUpdated(const int32_t &track_id, const uint32_t &segment_number) +void LLHlsStream::OnMediaSegmentCreated(const int32_t &track_id, const uint32_t &segment_number) { // Check whether at least one segment of every track has been created. CheckPlaylistReady(); @@ -1190,23 +1210,17 @@ void LLHlsStream::OnMediaSegmentUpdated(const int32_t &track_id, const uint32_t return; } - // Timescale to seconds(decimal) - auto segment_duration = static_cast(segment->GetDuration()) / static_cast(1000.0); - - auto start_timestamp_ms = (static_cast(segment->GetStartTimestamp()) / GetTrack(track_id)->GetTimeBase().GetTimescale()) * 1000.0; - auto start_timestamp = std::chrono::duration_cast(GetInputStreamCreatedTime().time_since_epoch()).count() + start_timestamp_ms; - - auto segment_info = LLHlsChunklist::SegmentInfo(segment->GetNumber(), start_timestamp, segment_duration, - segment->GetSize(), GetSegmentName(track_id, segment->GetNumber()), "", true); + // Empty segment + auto segment_info = LLHlsChunklist::SegmentInfo(segment->GetNumber(), GetSegmentName(track_id, segment->GetNumber())); - playlist->AppendSegmentInfo(segment_info); + playlist->CreateSegmentInfo(segment_info); - logtd("Media segment updated : track_id = %d, segment_number = %d, start_timestamp = %llu, segment_duration = %f", track_id, segment_number, segment->GetStartTimestamp(), segment_duration); + logtd("Media segment updated : track_id = %d, segment_number = %d", track_id, segment_number); DumpSegmentOfAllItems(track_id, segment_number); } -void LLHlsStream::OnMediaChunkUpdated(const int32_t &track_id, const uint32_t &segment_number, const uint32_t &chunk_number) +void LLHlsStream::OnMediaChunkUpdated(const int32_t &track_id, const uint32_t &segment_number, const uint32_t &chunk_number, bool last_chunk) { auto playlist = GetChunklistWriter(track_id); if (playlist == nullptr) @@ -1238,7 +1252,8 @@ void LLHlsStream::OnMediaChunkUpdated(const int32_t &track_id, const uint32_t &s auto chunk_info = LLHlsChunklist::SegmentInfo(chunk->GetNumber(), start_timestamp, chunk_duration, chunk->GetSize(), GetPartialSegmentName(track_id, segment_number, chunk->GetNumber()), - GetNextPartialSegmentName(track_id, segment_number, chunk->GetNumber()), chunk->IsIndependent()); + GetNextPartialSegmentName(track_id, segment_number, chunk->GetNumber(), last_chunk), + chunk->IsIndependent(), last_chunk); playlist->AppendPartialSegmentInfo(segment_number, chunk_info); diff --git a/src/projects/publishers/llhls/llhls_stream.h b/src/projects/publishers/llhls/llhls_stream.h index dd489d47c..c3d815e5f 100755 --- a/src/projects/publishers/llhls/llhls_stream.h +++ b/src/projects/publishers/llhls/llhls_stream.h @@ -91,8 +91,8 @@ class LLHlsStream : public pub::Stream, public bmff::FMp4StorageObserver // bmff::FMp4StorageObserver implementation void OnFMp4StorageInitialized(const int32_t &track_id) override; - void OnMediaSegmentUpdated(const int32_t &track_id, const uint32_t &segment_number) override; - void OnMediaChunkUpdated(const int32_t &track_id, const uint32_t &segment_number, const uint32_t &chunk_number) override; + void OnMediaSegmentCreated(const int32_t &track_id, const uint32_t &segment_number) override; + void OnMediaChunkUpdated(const int32_t &track_id, const uint32_t &segment_number, const uint32_t &chunk_number, bool last_chunk) override; void OnMediaSegmentDeleted(const int32_t &track_id, const uint32_t &segment_number) override; // Create and Get fMP4 packager and storage with track info, storage and packager_config @@ -111,7 +111,7 @@ class LLHlsStream : public pub::Stream, public bmff::FMp4StorageObserver ov::String GetInitializationSegmentName(const int32_t &track_id) const; ov::String GetSegmentName(const int32_t &track_id, const int64_t &segment_number) const; ov::String GetPartialSegmentName(const int32_t &track_id, const int64_t &segment_number, const int64_t &partial_number) const; - ov::String GetNextPartialSegmentName(const int32_t &track_id, const int64_t &segment_number, const int64_t &partial_number) const; + ov::String GetNextPartialSegmentName(const int32_t &track_id, const int64_t &segment_number, const int64_t &partial_number, bool last_chunk) const; bool AppendMediaPacket(const std::shared_ptr &media_packet); @@ -146,6 +146,7 @@ class LLHlsStream : public pub::Stream, public bmff::FMp4StorageObserver uint64_t _min_chunk_duration_ms = std::numeric_limits::max(); double _configured_part_hold_back = 0; + bool _preload_hint_enabled = true; std::map> _master_playlists; std::mutex _master_playlists_lock; diff --git a/src/projects/transcoder/transcoder_stream.cpp b/src/projects/transcoder/transcoder_stream.cpp index fb25304e0..ffb094d7c 100644 --- a/src/projects/transcoder/transcoder_stream.cpp +++ b/src/projects/transcoder/transcoder_stream.cpp @@ -1356,6 +1356,39 @@ void TranscoderStream::OnFilteredFrame(int32_t filter_id, std::shared_ptrSetTrackId(filter_id); +#if 0 // For debugging + if(filtered_frame != nullptr && filtered_frame->GetMediaType() == cmn::MediaType::Video) + { + auto av_frame = (AVFrame*)filtered_frame->GetPrivData(); + if(av_frame == nullptr) + { + logte("%s Invalid pointer", _log_prefix.CStr()); + return; + } + + int l = 0, t = 0, w = 100, h = 100; + + if (filtered_frame->GetHeight() == 1080) + { + l = 0; t = 0; + } + else if (filtered_frame->GetHeight() == 720) + { + l = 100; t = 100; + } + else if (filtered_frame->GetHeight() == 360) + { + l = 200; t = 200; + } + + for(int y = t ; y < t+h ; y++) + { + uint8_t *ptr = av_frame->data[0] + y * av_frame->linesize[0] + l; + + memset(ptr, 125, w); + } + } +#endif EncodeFrame(std::move(filtered_frame)); }