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