1 /* 2 * Copyright (c) 2024-2024 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16 #ifndef HISTREAMER_CACHED_MEDIA_BUFFER_H 17 #define HISTREAMER_CACHED_MEDIA_BUFFER_H 18 19 #include <cstdint> 20 #include <memory> 21 #include <mutex> 22 #include <list> 23 #include <chrono> 24 25 #include "common/log.h" 26 #include "lru_cache.h" 27 #include "osal/utils/steady_clock.h" 28 29 namespace OHOS { 30 namespace Media { 31 constexpr uint32_t CHUNK_SIZE = 16 * 1024; 32 constexpr uint64_t MAX_CACHE_BUFFER_SIZE = 19 * 1024 * 1024; 33 constexpr int64_t LOOP_TIMEOUT = 60; // s 34 constexpr OHOS::HiviewDFX::HiLogLabel LABEL = { LOG_CORE, LOG_DOMAIN_STREAM_SOURCE, "HiStreamer" }; 35 36 using Clock = std::chrono::steady_clock; 37 using TimePoint = Clock::time_point; 38 using namespace std::chrono; 39 40 struct CacheChunk { 41 uint32_t chunkSize; 42 uint32_t dataLength; 43 uint64_t offset; 44 uint8_t data[]; 45 }; 46 47 using CacheChunkList = std::list<CacheChunk*>; 48 using ChunkIterator = CacheChunkList::iterator; 49 50 struct FragmentCacheBuffer { 51 uint64_t offsetBegin; 52 int64_t dataLength; 53 int64_t accessLength; 54 uint64_t totalReadSize; 55 TimePoint readTime; 56 CacheChunkList chunks; 57 ChunkIterator accessPos; 58 bool isSplit {false}; 59 offsetBeginFragmentCacheBuffer60 explicit FragmentCacheBuffer(uint64_t offset = 0) : offsetBegin(offset), dataLength(0), 61 accessLength(0), totalReadSize(0), readTime(Clock::now()) 62 { 63 accessPos = chunks.end(); 64 } 65 ~FragmentCacheBufferFragmentCacheBuffer66 ~FragmentCacheBuffer() 67 { 68 chunks.clear(); 69 } 70 }; 71 72 using FragmentCacheBufferList = std::list<FragmentCacheBuffer>; 73 using FragmentIterator = FragmentCacheBufferList::iterator; 74 75 class CacheMediaChunkBufferImpl { 76 public: 77 CacheMediaChunkBufferImpl(); 78 virtual ~CacheMediaChunkBufferImpl(); 79 80 CacheMediaChunkBufferImpl(const CacheMediaChunkBufferImpl&) = delete; 81 CacheMediaChunkBufferImpl(CacheMediaChunkBufferImpl&&) = delete; 82 const CacheMediaChunkBufferImpl& operator=(const CacheMediaChunkBufferImpl&) = delete; 83 CacheMediaChunkBufferImpl& operator=(CacheMediaChunkBufferImpl&&) = delete; 84 85 bool Init(uint64_t totalBuffSize, uint32_t chunkSize); 86 size_t Read(void* ptr, uint64_t offset, size_t readSize); 87 virtual size_t Write(void* ptr, uint64_t inOffset, size_t inWriteSize); 88 bool Seek(uint64_t offset); 89 size_t GetBufferSize(uint64_t offset); 90 uint64_t GetNextBufferOffset(uint64_t offset); 91 void Dump(uint64_t param); 92 bool Check(); 93 void Clear(); 94 uint64_t GetFreeSize(); 95 bool ClearFragmentBeforeOffset(uint64_t offset); 96 bool ClearChunksOfFragment(uint64_t offset); 97 bool ClearMiddleReadFragment(uint64_t minOffset, uint64_t maxOffset); 98 bool IsReadSplit(uint64_t offset); 99 void SetIsLargeOffsetSpan(bool isLargeOffsetSpan); 100 101 protected: 102 virtual CacheChunk* GetFreeCacheChunk(uint64_t offset, bool checkAllowFailContinue = false); 103 virtual ChunkIterator AddFragmentCacheBuffer(uint64_t offset); 104 FragmentIterator GetFragmentIterator(FragmentIterator& currFragmentIter, 105 uint64_t offset, ChunkIterator chunkPos, CacheChunk* splitHead, CacheChunk*& chunkInfo); 106 virtual ChunkIterator SplitFragmentCacheBuffer(FragmentIterator& currFragmentIter, 107 uint64_t offset, ChunkIterator chunkPos); 108 void DeleteHasReadFragmentCacheBuffer(FragmentIterator& fragmentIter, size_t allowChunkNum); 109 FragmentIterator EraseFragmentCache(const FragmentIterator& iter); 110 FragmentIterator GetOffsetFragmentCache(FragmentIterator& fragmentPos, uint64_t offset); 111 ChunkIterator GetOffsetChunkCache(CacheChunkList& fragmentCacheBuffer, uint64_t offset); 112 void DumpInner(uint64_t param); 113 bool CheckInner(); 114 void CheckFragment(const FragmentCacheBuffer& fragment, bool& checkSuccess); 115 bool DumpAndCheckInner(); 116 static void UpdateAccessPos(FragmentIterator& fragmentPos, ChunkIterator& chunkPos, uint64_t offsetChunk); 117 bool WriteInPlace(FragmentIterator& fragmentPos, uint8_t* ptr, uint64_t inOffset, 118 size_t inWriteSize, size_t& outWriteSize); 119 bool WriteMergerPre(uint64_t offset, size_t writeSize, FragmentIterator& nextFragmentPos); 120 void WriteMergerPost(FragmentIterator& nextFragmentPos); 121 size_t ReadInner(void* ptr, uint64_t offset, size_t readSize); 122 123 template<typename Pred> 124 // Search for the fragment pointed to by the offset. GetOffsetFragmentCache(FragmentIterator & fragmentPos,uint64_t offset,Pred pred)125 FragmentIterator GetOffsetFragmentCache(FragmentIterator& fragmentPos, uint64_t offset, Pred pred) 126 { 127 if (fragmentPos != fragmentCacheBuffer_.end()) { 128 if (pred(offset, fragmentPos->offsetBegin, fragmentPos->offsetBegin + fragmentPos->dataLength)) { 129 return fragmentPos; 130 } 131 } 132 int64_t loopStartTime = loopInterruptClock_.ElapsedSeconds(); 133 bool isTimeout = false; 134 135 auto fragmentCachePos = std::find_if(fragmentCacheBuffer_.begin(), fragmentCacheBuffer_.end(), 136 [offset, pred, &isTimeout, &loopStartTime, this](const auto& fragment) { 137 int64_t now = this->loopInterruptClock_.ElapsedSeconds(); 138 int64_t loopDuration = now > loopStartTime ? now - loopStartTime : 0; 139 if (loopDuration > LOOP_TIMEOUT) { 140 isTimeout = true; 141 MEDIA_LOG_E("loop timeout"); 142 return true; 143 } 144 if (pred(offset, fragment.offsetBegin, fragment.offsetBegin + fragment.dataLength)) { 145 return true; 146 } 147 return false; 148 }); 149 if (isTimeout) { 150 return fragmentCacheBuffer_.end(); 151 } 152 return fragmentCachePos; 153 } 154 155 template<typename Pred> 156 // Search for the chunk pointed to by the offset. GetOffsetChunkCache(CacheChunkList & chunkCaches,uint64_t offset,Pred pred)157 static ChunkIterator GetOffsetChunkCache(CacheChunkList& chunkCaches, uint64_t offset, Pred pred) 158 { 159 auto chunkCachePos = std::find_if(chunkCaches.begin(), chunkCaches.end(), 160 [offset, pred](const auto& fragment) { 161 if (pred(offset, fragment->offset, fragment->offset + fragment->dataLength)) { 162 return true; 163 } 164 return false; 165 }); 166 return chunkCachePos; 167 } 168 169 size_t WriteChunk(FragmentCacheBuffer& fragmentCacheBuffer, ChunkIterator& chunkPos, 170 void* ptr, uint64_t offset, size_t writeSize); 171 bool CheckThresholdFragmentCacheBuffer(FragmentIterator& currWritePos); 172 void DeleteUnreadFragmentCacheBuffer(FragmentIterator& fragmentIter, size_t allowChunkNum); CalcAllowMaxChunkNum(uint64_t fragmentReadSize,uint64_t offset)173 size_t CalcAllowMaxChunkNum(uint64_t fragmentReadSize, uint64_t offset) 174 { 175 size_t allowNum = static_cast<size_t>((static_cast<double>(fragmentReadSize) / 176 static_cast<double>(totalReadSize_)) * chunkMaxNum_); 177 return allowNum; 178 } 179 void ResetReadSizeAlloc(); 180 CacheChunk* UpdateFragmentCacheForDelHead(FragmentIterator& fragmentIter); 181 void HandleFragmentPos(FragmentIterator& fragmentIter); 182 void UpdateFragment(FragmentIterator& fragmentPos, size_t hasReadSize, uint64_t offsetChunk); 183 bool CheckLoopTimeout(int64_t loopStartTime); 184 185 protected: 186 std::mutex mutex_; 187 FragmentIterator readPos_; 188 FragmentIterator writePos_; 189 uint64_t totalBuffSize_ {0}; 190 uint64_t totalReadSize_ {0}; 191 uint32_t chunkMaxNum_ {0}; 192 uint32_t chunkSize_ {0}; 193 double initReadSizeFactor_ {0.0}; 194 uint8_t* bufferAddr_ {nullptr}; 195 FragmentCacheBufferList fragmentCacheBuffer_; 196 CacheChunkList freeChunks_; 197 size_t fragmentMaxNum_; 198 LruCache<int64_t, FragmentIterator> lruCache_; 199 200 bool isLargeOffsetSpan_ {false}; 201 SteadyClock loopInterruptClock_; 202 }; 203 204 class CacheMediaBuffer { 205 public: 206 CacheMediaBuffer() = default; 207 virtual ~CacheMediaBuffer() = default; 208 209 virtual bool Init(uint64_t totalBuffSize, uint32_t chunkSize) = 0; 210 virtual size_t Read(void* ptr, uint64_t offset, size_t readSize) = 0; 211 virtual size_t Write(void* ptr, uint64_t offset, size_t writeSize) = 0; 212 virtual bool Seek(uint64_t offset) = 0; 213 virtual size_t GetBufferSize(uint64_t offset) = 0; 214 virtual uint64_t GetNextBufferOffset(uint64_t offset) = 0; 215 virtual void Clear() = 0; 216 virtual void SetReadBlocking(bool isReadBlockingAllowed) = 0; 217 virtual void Dump(uint64_t param) = 0; 218 virtual uint64_t GetFreeSize() = 0; 219 virtual bool ClearFragmentBeforeOffset(uint64_t offset) = 0; 220 virtual bool ClearChunksOfFragment(uint64_t offset) = 0; 221 virtual bool ClearMiddleReadFragment(uint64_t minOffset, uint64_t maxOffset) = 0; 222 virtual bool IsReadSplit(uint64_t offset) = 0; 223 virtual void SetIsLargeOffsetSpan(bool isLargeOffsetSpan) = 0; 224 }; 225 226 class CacheMediaChunkBufferImpl; 227 class CacheMediaChunkBuffer : public CacheMediaBuffer { 228 public: 229 CacheMediaChunkBuffer(); 230 ~CacheMediaChunkBuffer() override; 231 CacheMediaChunkBuffer(const CacheMediaChunkBuffer&) = delete; 232 CacheMediaChunkBuffer(CacheMediaChunkBuffer&&) = delete; 233 const CacheMediaChunkBuffer& operator=(const CacheMediaChunkBuffer&) = delete; 234 CacheMediaChunkBuffer& operator=(CacheMediaChunkBuffer&&) = delete; 235 236 bool Init(uint64_t totalBuffSize, uint32_t chunkSize) override; 237 size_t Read(void* ptr, uint64_t offset, size_t readSize) override; 238 size_t Write(void* ptr, uint64_t offset, size_t writeSize) override; 239 bool Seek(uint64_t offset) override; 240 size_t GetBufferSize(uint64_t offset) override; 241 uint64_t GetNextBufferOffset(uint64_t offset) override; 242 void Clear() override; 243 void SetReadBlocking(bool isReadBlockingAllowed) override; 244 void Dump(uint64_t param) override; 245 bool Check(); 246 uint64_t GetFreeSize() override; 247 bool ClearFragmentBeforeOffset(uint64_t offset) override; 248 bool ClearChunksOfFragment(uint64_t offset) override; 249 bool ClearMiddleReadFragment(uint64_t minOffset, uint64_t maxOffset) override; 250 bool IsReadSplit(uint64_t offset) override; 251 void SetIsLargeOffsetSpan(bool isLargeOffsetSpan) override; 252 private: 253 std::unique_ptr<CacheMediaChunkBufferImpl> impl_; 254 }; 255 256 class CacheMediaChunkBufferHlsImpl : public CacheMediaChunkBufferImpl { 257 protected: 258 CacheChunk* GetFreeCacheChunk(uint64_t offset, bool checkAllowFailContinue = false) override; 259 ChunkIterator SplitFragmentCacheBuffer(FragmentIterator& currFragmentIter, uint64_t offset, 260 ChunkIterator chunkPos) override; 261 ChunkIterator AddFragmentCacheBuffer(uint64_t offset) override; 262 size_t Write(void* ptr, uint64_t inOffset, size_t inWriteSize) override; 263 }; 264 265 } 266 } 267 #endif