• 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 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