• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2023 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 #include "parameter.h"
17 #include "soundpool.h"
18 #include "media_log.h"
19 #include "media_errors.h"
20 #include "stream_id_manager.h"
21 
22 namespace {
23     // audiorender max concurrency.
24     static const std::string THREAD_POOL_NAME = "StreamIDManagerThreadPool";
25     static const int32_t MAX_THREADS_NUM = std::thread::hardware_concurrency() >= 4 ? 2 : 1;
26 }
27 
28 namespace OHOS {
29 namespace Media {
StreamIDManager(int32_t maxStreams,AudioStandard::AudioRendererInfo audioRenderInfo)30 StreamIDManager::StreamIDManager(int32_t maxStreams,
31     AudioStandard::AudioRendererInfo audioRenderInfo) : audioRendererInfo_(audioRenderInfo), maxStreams_(maxStreams)
32 {
33     MEDIA_LOGI("Construction StreamIDManager.");
34 #ifdef SOUNDPOOL_SUPPORT_LOW_LATENCY
35     char hardwareName[10] = {0};
36     GetParameter("ohos.boot.hardware", "rk3568", hardwareName, sizeof(hardwareName));
37     if (strcmp(hardwareName, "baltimore") != 0) {
38         MEDIA_LOGI("Device unsupport low-latency, force set to normal play.");
39         audioRendererInfo_.rendererFlags = 0;
40     } else {
41         MEDIA_LOGI("Device support low-latency, set renderer by user.");
42     }
43 #else
44     // Force all play to normal.
45     audioRendererInfo_.rendererFlags = 0;
46     MEDIA_LOGI("SoundPool unsupport low-latency.");
47 #endif
48 
49     InitThreadPool();
50 }
51 
~StreamIDManager()52 StreamIDManager::~StreamIDManager()
53 {
54     MEDIA_LOGI("Destruction StreamIDManager");
55     if (callback_ != nullptr) {
56         callback_.reset();
57     }
58     if (frameWriteCallback_ != nullptr) {
59         frameWriteCallback_.reset();
60     }
61     for (auto cacheBuffer : cacheBuffers_) {
62         if (cacheBuffer.second != nullptr) {
63             cacheBuffer.second->Release();
64         }
65     }
66     cacheBuffers_.clear();
67     if (isStreamPlayingThreadPoolStarted_.load()) {
68         if (streamPlayingThreadPool_ != nullptr) {
69             streamPlayingThreadPool_->Stop();
70         }
71         isStreamPlayingThreadPoolStarted_.store(false);
72     }
73 }
74 
InitThreadPool()75 int32_t StreamIDManager::InitThreadPool()
76 {
77     if (isStreamPlayingThreadPoolStarted_.load()) {
78         return MSERR_OK;
79     }
80     streamPlayingThreadPool_ = std::make_unique<ThreadPool>(THREAD_POOL_NAME);
81     CHECK_AND_RETURN_RET_LOG(streamPlayingThreadPool_ != nullptr, MSERR_INVALID_VAL,
82         "Failed to obtain playing ThreadPool");
83     if (maxStreams_ > MAX_PLAY_STREAMS_NUMBER) {
84         maxStreams_ = MAX_PLAY_STREAMS_NUMBER;
85         MEDIA_LOGI("more than max play stream number, align to max play strem number.");
86     }
87     if (maxStreams_ < MIN_PLAY_STREAMS_NUMBER) {
88         maxStreams_ = MIN_PLAY_STREAMS_NUMBER;
89         MEDIA_LOGI("less than min play stream number, align to min play strem number.");
90     }
91     MEDIA_LOGI("stream playing thread pool maxStreams_:%{public}d", maxStreams_);
92     // For stream priority logic, thread num need align to task num.
93     streamPlayingThreadPool_->Start(maxStreams_);
94     streamPlayingThreadPool_->SetMaxTaskNum(maxStreams_);
95     isStreamPlayingThreadPoolStarted_.store(true);
96 
97     return MSERR_OK;
98 }
99 
Play(std::shared_ptr<SoundParser> soundParser,PlayParams playParameters)100 int32_t StreamIDManager::Play(std::shared_ptr<SoundParser> soundParser, PlayParams playParameters)
101 {
102     CHECK_AND_RETURN_RET_LOG(soundParser != nullptr, -1, "Invalid soundParser.");
103     int32_t soundID = soundParser->GetSoundID();
104     int32_t streamID = GetFreshStreamID(soundID, playParameters);
105     {
106         std::lock_guard lock(streamIDManagerLock_);
107         if (streamID <= 0) {
108             do {
109                 nextStreamID_ = nextStreamID_ == INT32_MAX ? 1 : nextStreamID_ + 1;
110             } while (FindCacheBuffer(nextStreamID_) != nullptr);
111             streamID = nextStreamID_;
112             std::deque<std::shared_ptr<AudioBufferEntry>> cacheData;
113             soundParser->GetSoundData(cacheData);
114             size_t cacheDataTotalSize = soundParser->GetSoundDataTotalSize();
115             auto cacheBuffer =
116                 std::make_shared<CacheBuffer>(soundParser->GetSoundTrackFormat(), cacheData, cacheDataTotalSize,
117                      soundID, streamID);
118             CHECK_AND_RETURN_RET_LOG(cacheBuffer != nullptr, -1, "failed to create cache buffer");
119             CHECK_AND_RETURN_RET_LOG(callback_ != nullptr, MSERR_INVALID_VAL, "Invalid callback.");
120             cacheBuffer->SetCallback(callback_);
121             cacheBufferCallback_ = std::make_shared<CacheBufferCallBack>(weak_from_this());
122             CHECK_AND_RETURN_RET_LOG(cacheBufferCallback_ != nullptr, MSERR_INVALID_VAL,
123                 "Invalid cachebuffer callback");
124             cacheBuffer->SetCacheBufferCallback(cacheBufferCallback_);
125             if (frameWriteCallback_ != nullptr) {
126                 cacheBuffer->SetFrameWriteCallback(frameWriteCallback_);
127             }
128             cacheBuffers_.emplace(streamID, cacheBuffer);
129         }
130     }
131     SetPlay(soundID, streamID, playParameters);
132     return streamID;
133 }
134 
SetPlay(const int32_t soundID,const int32_t streamID,const PlayParams playParameters)135 int32_t StreamIDManager::SetPlay(const int32_t soundID, const int32_t streamID, const PlayParams playParameters)
136 {
137     if (!isStreamPlayingThreadPoolStarted_.load()) {
138         InitThreadPool();
139     }
140 
141     CHECK_AND_RETURN_RET_LOG(streamPlayingThreadPool_ != nullptr, MSERR_INVALID_VAL,
142         "Failed to obtain stream play threadpool.");
143     MEDIA_LOGI("StreamIDManager cur task num:%{public}zu, maxStreams_:%{public}d",
144         currentTaskNum_, maxStreams_);
145     // CacheBuffer must prepare before play.
146     std::shared_ptr<CacheBuffer> freshCacheBuffer = FindCacheBuffer(streamID);
147     CHECK_AND_RETURN_RET_LOG(freshCacheBuffer != nullptr, -1, "Invalid fresh cache buffer");
148     freshCacheBuffer->PreparePlay(streamID, audioRendererInfo_, playParameters);
149     int32_t tempMaxStream = maxStreams_;
150     if (currentTaskNum_ < static_cast<size_t>(tempMaxStream)) {
151         AddPlayTask(streamID, playParameters);
152     } else {
153         int32_t playingStreamID = playingStreamIDs_.back();
154         std::shared_ptr<CacheBuffer> playingCacheBuffer = FindCacheBuffer(playingStreamID);
155         CHECK_AND_RETURN_RET_LOG(freshCacheBuffer != nullptr, -1, "Invalid fresh cache buffer");
156         MEDIA_LOGI("StreamIDManager fresh sound priority:%{public}d, playing stream priority:%{public}d",
157             freshCacheBuffer->GetPriority(), playingCacheBuffer->GetPriority());
158         if (freshCacheBuffer->GetPriority() >= playingCacheBuffer->GetPriority()) {
159             MEDIA_LOGI("StreamIDManager stop playing low priority sound:%{public}d", playingStreamID);
160             playingCacheBuffer->Stop(playingStreamID);
161             playingStreamIDs_.pop_back();
162             MEDIA_LOGI("StreamIDManager to playing fresh sound:%{public}d.", streamID);
163             AddPlayTask(streamID, playParameters);
164         } else {
165             std::lock_guard lock(streamIDManagerLock_);
166             MEDIA_LOGI("StreamIDManager queue will play streams, streamID:%{public}d.", streamID);
167             StreamIDAndPlayParamsInfo freshStreamIDAndPlayParamsInfo;
168             freshStreamIDAndPlayParamsInfo.streamID = streamID;
169             freshStreamIDAndPlayParamsInfo.playParameters = playParameters;
170             QueueAndSortWillPlayStreamID(freshStreamIDAndPlayParamsInfo);
171         }
172     }
173 
174     return MSERR_OK;
175 }
176 
177 // Sort in descending order
178 // 0 has the lowest priority, and the higher the value, the higher the priority
179 // The queue head has the highest value and priority
QueueAndSortPlayingStreamID(int32_t streamID)180 void StreamIDManager::QueueAndSortPlayingStreamID(int32_t streamID)
181 {
182     if (playingStreamIDs_.empty()) {
183         playingStreamIDs_.emplace_back(streamID);
184     } else {
185         bool shouldReCombinePlayingQueue = false;
186         for (size_t i = 0; i < playingStreamIDs_.size(); i++) {
187             int32_t playingStreamID = playingStreamIDs_[i];
188             std::shared_ptr<CacheBuffer> freshCacheBuffer = FindCacheBuffer(streamID);
189             std::shared_ptr<CacheBuffer> playingCacheBuffer = FindCacheBuffer(playingStreamID);
190             if (playingCacheBuffer == nullptr) {
191                 playingStreamIDs_.erase(playingStreamIDs_.begin() + i);
192                 shouldReCombinePlayingQueue = true;
193                 break;
194             }
195             if (!playingCacheBuffer->IsRunning()) {
196                 playingStreamIDs_.erase(playingStreamIDs_.begin() + i);
197                 shouldReCombinePlayingQueue = true;
198                 break;
199             }
200             if (freshCacheBuffer == nullptr) {
201                 break;
202             }
203             if (freshCacheBuffer->GetPriority() <= playingCacheBuffer->GetPriority()) {
204                 playingStreamIDs_.insert(playingStreamIDs_.begin() + i, streamID);
205                 break;
206             }
207         }
208         if (shouldReCombinePlayingQueue) {
209             QueueAndSortPlayingStreamID(streamID);
210         }
211     }
212 }
213 
214 // Sort in descending order.
215 // 0 has the lowest priority, and the higher the value, the higher the priority
216 // The queue head has the highest value and priority
QueueAndSortWillPlayStreamID(StreamIDAndPlayParamsInfo freshStreamIDAndPlayParamsInfo)217 void StreamIDManager::QueueAndSortWillPlayStreamID(StreamIDAndPlayParamsInfo freshStreamIDAndPlayParamsInfo)
218 {
219     if (willPlayStreamInfos_.empty()) {
220         willPlayStreamInfos_.emplace_back(freshStreamIDAndPlayParamsInfo);
221     } else {
222         bool shouldReCombineWillPlayQueue = false;
223         for (size_t i = 0; i < willPlayStreamInfos_.size(); i++) {
224             std::shared_ptr<CacheBuffer> freshCacheBuffer = FindCacheBuffer(freshStreamIDAndPlayParamsInfo.streamID);
225             std::shared_ptr<CacheBuffer> willPlayCacheBuffer = FindCacheBuffer(willPlayStreamInfos_[i].streamID);
226             if (willPlayCacheBuffer == nullptr) {
227                 willPlayStreamInfos_.erase(willPlayStreamInfos_.begin() + i);
228                 shouldReCombineWillPlayQueue = true;
229                 break;
230             }
231             if (freshCacheBuffer == nullptr) {
232                 break;
233             }
234             if (freshCacheBuffer->GetPriority() <= willPlayCacheBuffer->GetPriority()) {
235                 willPlayStreamInfos_.insert(willPlayStreamInfos_.begin() + i, freshStreamIDAndPlayParamsInfo);
236                 break;
237             }
238         }
239         if (shouldReCombineWillPlayQueue) {
240             QueueAndSortWillPlayStreamID(freshStreamIDAndPlayParamsInfo);
241         }
242     }
243 }
244 
AddPlayTask(const int32_t streamID,const PlayParams playParameters)245 int32_t StreamIDManager::AddPlayTask(const int32_t streamID, const PlayParams playParameters)
246 {
247     ThreadPool::Task streamPlayTask = std::bind(&StreamIDManager::DoPlay, this, streamID);
248     CHECK_AND_RETURN_RET_LOG(streamPlayingThreadPool_ != nullptr, MSERR_INVALID_VAL,
249         "Failed to obtain playing ThreadPool");
250     CHECK_AND_RETURN_RET_LOG(streamPlayTask != nullptr, MSERR_INVALID_VAL, "Failed to obtain stream play Task");
251     streamPlayingThreadPool_->AddTask(streamPlayTask);
252     std::lock_guard lock(streamIDManagerLock_);
253     currentTaskNum_++;
254     QueueAndSortPlayingStreamID(streamID);
255     return MSERR_OK;
256 }
257 
DoPlay(const int32_t streamID)258 int32_t StreamIDManager::DoPlay(const int32_t streamID)
259 {
260     MEDIA_LOGI("StreamIDManager streamID:%{public}d", streamID);
261     std::shared_ptr<CacheBuffer> cacheBuffer = FindCacheBuffer(streamID);
262     CHECK_AND_RETURN_RET_LOG(cacheBuffer.get() != nullptr, MSERR_INVALID_VAL, "cachebuffer invalid.");
263     if (cacheBuffer->DoPlay(streamID) == MSERR_OK) {
264         return MSERR_OK;
265     }
266     return MSERR_INVALID_VAL;
267 }
268 
FindCacheBuffer(const int32_t streamID)269 std::shared_ptr<CacheBuffer> StreamIDManager::FindCacheBuffer(const int32_t streamID)
270 {
271     if (cacheBuffers_.empty()) {
272         MEDIA_LOGI("StreamIDManager cacheBuffers_ empty");
273         return nullptr;
274     }
275     if (cacheBuffers_.find(streamID) != cacheBuffers_.end()) {
276         return cacheBuffers_.at(streamID);
277     }
278     return nullptr;
279 }
280 
GetStreamIDBySoundID(const int32_t soundID)281 int32_t StreamIDManager::GetStreamIDBySoundID(const int32_t soundID)
282 {
283     PlayParams playParameters;
284     return GetFreshStreamID(soundID, playParameters);
285 }
286 
GetFreshStreamID(const int32_t soundID,PlayParams playParameters)287 int32_t StreamIDManager::GetFreshStreamID(const int32_t soundID, PlayParams playParameters)
288 {
289     int32_t streamID = 0;
290     if (cacheBuffers_.empty()) {
291         MEDIA_LOGI("StreamIDManager cacheBuffers_ empty");
292         return streamID;
293     }
294     for (auto cacheBuffer : cacheBuffers_) {
295         if (cacheBuffer.second == nullptr) {
296             MEDIA_LOGE("Invalid cacheBuffer, soundID:%{public}d", soundID);
297             continue;
298         }
299         if (soundID == cacheBuffer.second->GetSoundID()) {
300             streamID = cacheBuffer.second->GetStreamID();
301             MEDIA_LOGI("Have cache soundID:%{public}d, streamID:%{public}d", soundID, streamID);
302             break;
303         }
304     }
305     return streamID;
306 }
307 
OnPlayFinished()308 void StreamIDManager::OnPlayFinished()
309 {
310     currentTaskNum_--;
311     if (!willPlayStreamInfos_.empty()) {
312         MEDIA_LOGI("StreamIDManager OnPlayFinished will play streams non empty, get the front.");
313         StreamIDAndPlayParamsInfo willPlayStreamInfo =  willPlayStreamInfos_.front();
314         AddPlayTask(willPlayStreamInfo.streamID, willPlayStreamInfo.playParameters);
315         std::lock_guard lock(streamIDManagerLock_);
316         willPlayStreamInfos_.pop_front();
317     }
318 }
319 
SetCallback(const std::shared_ptr<ISoundPoolCallback> & callback)320 int32_t StreamIDManager::SetCallback(const std::shared_ptr<ISoundPoolCallback> &callback)
321 {
322     callback_ = callback;
323     return MSERR_OK;
324 }
325 
SetFrameWriteCallback(const std::shared_ptr<ISoundPoolFrameWriteCallback> & callback)326 int32_t StreamIDManager::SetFrameWriteCallback(const std::shared_ptr<ISoundPoolFrameWriteCallback> &callback)
327 {
328     frameWriteCallback_ = callback;
329     return MSERR_OK;
330 }
331 } // namespace Media
332 } // namespace OHOS
333