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