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 using Clock = std::chrono::steady_clock; 36 using TimePoint = Clock::time_point; 37 using namespace std::chrono; 38 39 struct CacheChunk { 40 uint32_t chunkSize; 41 uint32_t dataLength; 42 uint64_t offset; 43 uint8_t data[]; 44 }; 45 46 using CacheChunkList = std::list<CacheChunk*>; 47 using ChunkIterator = CacheChunkList::iterator; 48 49 struct FragmentCacheBuffer { 50 uint64_t offsetBegin; 51 int64_t dataLength; 52 int64_t accessLength; 53 uint64_t totalReadSize; 54 TimePoint readTime; 55 CacheChunkList chunks; 56 ChunkIterator accessPos; 57 bool isSplit {false}; 58 offsetBeginFragmentCacheBuffer59 explicit FragmentCacheBuffer(uint64_t offset = 0) : offsetBegin(offset), dataLength(0), 60 accessLength(0), totalReadSize(0), readTime(Clock::now()) 61 { 62 accessPos = chunks.end(); 63 } 64 ~FragmentCacheBufferFragmentCacheBuffer65 ~FragmentCacheBuffer() 66 { 67 chunks.clear(); 68 } 69 }; 70 71 using FragmentCacheBufferList = std::list<FragmentCacheBuffer>; 72 using FragmentIterator = FragmentCacheBufferList::iterator; 73 74 class CacheMediaChunkBufferImpl { 75 public: 76 CacheMediaChunkBufferImpl(); 77 virtual ~CacheMediaChunkBufferImpl(); 78 79 CacheMediaChunkBufferImpl(const CacheMediaChunkBufferImpl&) = delete; 80 CacheMediaChunkBufferImpl(CacheMediaChunkBufferImpl&&) = delete; 81 const CacheMediaChunkBufferImpl& operator=(const CacheMediaChunkBufferImpl&) = delete; 82 CacheMediaChunkBufferImpl& operator=(CacheMediaChunkBufferImpl&&) = delete; 83 84 bool Init(uint64_t totalBuffSize, uint32_t chunkSize); 85 size_t Read(void* ptr, uint64_t offset, size_t readSize); 86 virtual size_t Write(void* ptr, uint64_t inOffset, size_t inWriteSize); 87 bool Seek(uint64_t offset); 88 size_t GetBufferSize(uint64_t offset); 89 uint64_t GetNextBufferOffset(uint64_t offset); 90 void Dump(uint64_t param); 91 bool Check(); 92 void Clear(); 93 uint64_t GetFreeSize(); 94 bool ClearFragmentBeforeOffset(uint64_t offset); 95 bool ClearChunksOfFragment(uint64_t offset); 96 bool ClearMiddleReadFragment(uint64_t minOffset, uint64_t maxOffset); 97 bool IsReadSplit(uint64_t offset); 98 void SetIsLargeOffsetSpan(bool isLargeOffsetSpan); 99 100 protected: 101 virtual CacheChunk* GetFreeCacheChunk(uint64_t offset, bool checkAllowFailContinue = false); 102 virtual ChunkIterator AddFragmentCacheBuffer(uint64_t offset); 103 FragmentIterator GetFragmentIterator(FragmentIterator& currFragmentIter, 104 uint64_t offset, ChunkIterator chunkPos, CacheChunk* splitHead, CacheChunk*& chunkInfo); 105 virtual ChunkIterator SplitFragmentCacheBuffer(FragmentIterator& currFragmentIter, 106 uint64_t offset, ChunkIterator chunkPos); 107 void DeleteHasReadFragmentCacheBuffer(FragmentIterator& fragmentIter, size_t allowChunkNum); 108 FragmentIterator EraseFragmentCache(const FragmentIterator& iter); 109 FragmentIterator GetOffsetFragmentCache(FragmentIterator& fragmentPos, uint64_t offset); 110 ChunkIterator GetOffsetChunkCache(CacheChunkList& fragmentCacheBuffer, uint64_t offset); 111 void DumpInner(uint64_t param); 112 bool CheckInner(); 113 void CheckFragment(const FragmentCacheBuffer& fragment, bool& checkSuccess); 114 bool DumpAndCheckInner(); 115 static void UpdateAccessPos(FragmentIterator& fragmentPos, ChunkIterator& chunkPos, uint64_t offsetChunk); 116 bool WriteInPlace(FragmentIterator& fragmentPos, uint8_t* ptr, uint64_t inOffset, 117 size_t inWriteSize, size_t& outWriteSize); 118 bool WriteMergerPre(uint64_t offset, size_t writeSize, FragmentIterator& nextFragmentPos); 119 void WriteMergerPost(FragmentIterator& nextFragmentPos); 120 size_t ReadInner(void* ptr, uint64_t offset, size_t readSize); 121 122 template<typename Pred> 123 // Search for the fragment pointed to by the offset. GetOffsetFragmentCache(FragmentIterator & fragmentPos,uint64_t offset,Pred pred)124 FragmentIterator GetOffsetFragmentCache(FragmentIterator& fragmentPos, uint64_t offset, Pred pred) 125 { 126 if (fragmentPos != fragmentCacheBuffer_.end()) { 127 if (pred(offset, fragmentPos->offsetBegin, fragmentPos->offsetBegin + fragmentPos->dataLength)) { 128 return fragmentPos; 129 } 130 } 131 int64_t loopStartTime = loopInterruptClock_.ElapsedSeconds(); 132 bool isTimeOut = false; 133 auto fragmentCachePos = std::find_if(fragmentCacheBuffer_.begin(), fragmentCacheBuffer_.end(), 134 [offset, pred, &isTimeOut, &loopStartTime, this](const auto& fragment) { 135 int64_t now = this->loopInterruptClock_.ElapsedSeconds(); 136 int64_t loopDuration = now > loopStartTime ? now - loopStartTime : 0; 137 if (loopDuration > LOOP_TIMEOUT) { 138 isTimeOut = true; 139 MEDIA_LOG_E("loop timeout"); 140 return true; 141 } 142 if (pred(offset, fragment.offsetBegin, fragment.offsetBegin + fragment.dataLength)) { 143 return true; 144 } 145 return false; 146 }); 147 if (isTimeOut) { 148 return fragmentCacheBuffer_.end(); 149 } 150 return fragmentCachePos; 151 } 152 153 template<typename Pred> 154 // Search for the chunk pointed to by the offset. GetOffsetChunkCache(CacheChunkList & chunkCaches,uint64_t offset,Pred pred)155 static ChunkIterator GetOffsetChunkCache(CacheChunkList& chunkCaches, uint64_t offset, Pred pred) 156 { 157 auto chunkCachePos = std::find_if(chunkCaches.begin(), chunkCaches.end(), 158 [offset, pred](const auto& fragment) { 159 if (pred(offset, fragment->offset, fragment->offset + fragment->dataLength)) { 160 return true; 161 } 162 return false; 163 }); 164 return chunkCachePos; 165 } 166 167 size_t WriteChunk(FragmentCacheBuffer& fragmentCacheBuffer, ChunkIterator& chunkPos, 168 void* ptr, uint64_t offset, size_t writeSize); 169 bool CheckThresholdFragmentCacheBuffer(FragmentIterator& currWritePos); 170 void DeleteUnreadFragmentCacheBuffer(FragmentIterator& fragmentIter, size_t allowChunkNum); CalcAllowMaxChunkNum(uint64_t fragmentReadSize,uint64_t offset)171 size_t CalcAllowMaxChunkNum(uint64_t fragmentReadSize, uint64_t offset) 172 { 173 size_t allowNum = static_cast<size_t>((static_cast<double>(fragmentReadSize) / 174 static_cast<double>(totalReadSize_)) * chunkMaxNum_); 175 return allowNum; 176 } 177 void ResetReadSizeAlloc(); 178 CacheChunk* UpdateFragmentCacheForDelHead(FragmentIterator& fragmentIter); 179 void HandleFragmentPos(FragmentIterator& fragmentIter); 180 void UpdateFragment(FragmentIterator& fragmentPos, size_t hasReadSize, uint64_t offsetChunk); 181 bool CheckLoopTimeout(int64_t loopStartTime); 182 183 protected: 184 std::mutex mutex_; 185 FragmentIterator readPos_; 186 FragmentIterator writePos_; 187 uint64_t totalBuffSize_ {0}; 188 uint64_t totalReadSize_ {0}; 189 uint32_t chunkMaxNum_ {0}; 190 uint32_t chunkSize_ {0}; 191 double initReadSizeFactor_ {0.0}; 192 uint8_t* bufferAddr_ {nullptr}; 193 FragmentCacheBufferList fragmentCacheBuffer_; 194 CacheChunkList freeChunks_; 195 size_t fragmentMaxNum_; 196 LruCache<int64_t, FragmentIterator> lruCache_; 197 198 bool isLargeOffsetSpan_ {false}; 199 SteadyClock loopInterruptClock_; 200 }; 201 202 class CacheMediaBuffer { 203 public: 204 CacheMediaBuffer() = default; 205 virtual ~CacheMediaBuffer() = default; 206 207 virtual bool Init(uint64_t totalBuffSize, uint32_t chunkSize) = 0; 208 virtual size_t Read(void* ptr, uint64_t offset, size_t readSize) = 0; 209 virtual size_t Write(void* ptr, uint64_t offset, size_t writeSize) = 0; 210 virtual bool Seek(uint64_t offset) = 0; 211 virtual size_t GetBufferSize(uint64_t offset) = 0; 212 virtual uint64_t GetNextBufferOffset(uint64_t offset) = 0; 213 virtual void Clear() = 0; 214 virtual void SetReadBlocking(bool isReadBlockingAllowed) = 0; 215 virtual void Dump(uint64_t param) = 0; 216 virtual uint64_t GetFreeSize() = 0; 217 virtual bool ClearFragmentBeforeOffset(uint64_t offset) = 0; 218 virtual bool ClearChunksOfFragment(uint64_t offset) = 0; 219 virtual bool ClearMiddleReadFragment(uint64_t minOffset, uint64_t maxOffset) = 0; 220 virtual bool IsReadSplit(uint64_t offset) = 0; 221 virtual void SetIsLargeOffsetSpan(bool isLargeOffsetSpan) = 0; 222 }; 223 224 class CacheMediaChunkBufferImpl; 225 class CacheMediaChunkBuffer : public CacheMediaBuffer { 226 public: 227 CacheMediaChunkBuffer(); 228 ~CacheMediaChunkBuffer() override; 229 CacheMediaChunkBuffer(const CacheMediaChunkBuffer&) = delete; 230 CacheMediaChunkBuffer(CacheMediaChunkBuffer&&) = delete; 231 const CacheMediaChunkBuffer& operator=(const CacheMediaChunkBuffer&) = delete; 232 CacheMediaChunkBuffer& operator=(CacheMediaChunkBuffer&&) = delete; 233 234 bool Init(uint64_t totalBuffSize, uint32_t chunkSize) override; 235 size_t Read(void* ptr, uint64_t offset, size_t readSize) override; 236 size_t Write(void* ptr, uint64_t offset, size_t writeSize) override; 237 bool Seek(uint64_t offset) override; 238 size_t GetBufferSize(uint64_t offset) override; 239 uint64_t GetNextBufferOffset(uint64_t offset) override; 240 void Clear() override; 241 void SetReadBlocking(bool isReadBlockingAllowed) override; 242 void Dump(uint64_t param) override; 243 bool Check(); 244 uint64_t GetFreeSize() override; 245 bool ClearFragmentBeforeOffset(uint64_t offset) override; 246 bool ClearChunksOfFragment(uint64_t offset) override; 247 bool ClearMiddleReadFragment(uint64_t minOffset, uint64_t maxOffset) override; 248 bool IsReadSplit(uint64_t offset) override; 249 void SetIsLargeOffsetSpan(bool isLargeOffsetSpan) override; 250 private: 251 std::unique_ptr<CacheMediaChunkBufferImpl> impl_; 252 }; 253 254 class CacheMediaChunkBufferHlsImpl : public CacheMediaChunkBufferImpl { 255 protected: 256 CacheChunk* GetFreeCacheChunk(uint64_t offset, bool checkAllowFailContinue = false) override; 257 ChunkIterator SplitFragmentCacheBuffer(FragmentIterator& currFragmentIter, uint64_t offset, 258 ChunkIterator chunkPos) override; 259 ChunkIterator AddFragmentCacheBuffer(uint64_t offset) override; 260 size_t Write(void* ptr, uint64_t inOffset, size_t inWriteSize) override; 261 }; 262 263 } 264 } 265 #endif