• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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