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 "media_log.h"
17 #include "media_errors.h"
18 #include "parameter.h"
19 #include "string_ex.h"
20 #include "sound_id_manager.h"
21 
22 namespace {
23     constexpr OHOS::HiviewDFX::HiLogLabel LABEL = {LOG_CORE, LOG_DOMAIN_SOUNDPOOL, "SoundIDManager"};
24     static const std::string THREAD_POOL_NAME = "OS_SoundMgr";
25     static const int32_t MAX_THREADS_NUM = std::thread::hardware_concurrency() >= 4 ? 2 : 1;
26 }
27 
28 namespace OHOS {
29 namespace Media {
SoundIDManager()30 SoundIDManager::SoundIDManager() : isParsingThreadPoolStarted_(false), quitQueue_(false)
31 {
32     MEDIA_LOGI("Construction SoundIDManager");
33     InitThreadPool();
34 }
35 
~SoundIDManager()36 SoundIDManager::~SoundIDManager()
37 {
38     MEDIA_LOGI("Destruction SoundIDManager");
39     {
40         std::lock_guard lock(soundManagerLock_);
41         quitQueue_ = true;
42         queueSpaceValid_.notify_all(); // notify all load waiters
43         queueDataValid_.notify_all();  // notify all worker threads
44     }
45 
46     if (callback_ != nullptr) {
47         callback_.reset();
48     }
49     {
50         std::lock_guard lock(soundManagerLock_);
51         for (auto soundParser : soundParsers_) {
52             if (soundParser.second != nullptr) {
53                 soundParser.second->Release();
54             }
55         }
56         soundParsers_.clear();
57     }
58 
59     if (isParsingThreadPoolStarted_) {
60         if (soundParserThreadPool_ != nullptr) {
61             soundParserThreadPool_->Stop();
62         }
63         isParsingThreadPoolStarted_ = false;
64     }
65 }
66 
InitThreadPool()67 int32_t SoundIDManager::InitThreadPool()
68 {
69     if (isParsingThreadPoolStarted_) {
70         return MSERR_OK;
71     }
72     soundParserThreadPool_ = std::make_unique<ThreadPool>(THREAD_POOL_NAME);
73     CHECK_AND_RETURN_RET_LOG(soundParserThreadPool_ != nullptr, MSERR_INVALID_VAL, "Failed to obtain ThreadPool");
74     soundParserThreadPool_->Start(MAX_THREADS_NUM);
75     isParsingThreadPoolStarted_ = true;
76 
77     return MSERR_OK;
78 }
79 
Load(std::string url)80 int32_t SoundIDManager::Load(std::string url)
81 {
82     int32_t soundID;
83     {
84         std::lock_guard lock(soundManagerLock_);
85         if (soundParsers_.size() >= MAX_LOAD_NUM) {
86             MEDIA_LOGI("SoundPool MAX_LOAD_NUM:%{public}zu.", MAX_LOAD_NUM);
87             return invalidSoundIDFlag;
88         }
89         const std::string fdHead = "fd://";
90         if (url.find(fdHead) == std::string::npos) {
91             return invalidSoundIDFlag;
92         }
93         int32_t fd = -1;
94         StrToInt(url.substr(fdHead.size()), fd);
95         if (fd < 0) {
96             return invalidSoundIDFlag;
97         }
98         do {
99             nextSoundID_ = nextSoundID_ == INT32_MAX ? 1 : nextSoundID_ + 1;
100         } while (FindSoundParser(nextSoundID_) != nullptr);
101         soundID = nextSoundID_;
102         auto soundParser = std::make_shared<SoundParser>(soundID, url);
103         CHECK_AND_RETURN_RET_LOG(soundParser != nullptr, -1, "failed to create soundParser");
104         soundParsers_.emplace(soundID, soundParser);
105     }
106     DoLoad(soundID);
107     return soundID;
108 }
109 
Load(int32_t fd,int64_t offset,int64_t length)110 int32_t SoundIDManager::Load(int32_t fd, int64_t offset, int64_t length)
111 {
112     int32_t soundID;
113     {
114         std::lock_guard lock(soundManagerLock_);
115         MEDIA_LOGI("SoundIDManager startLoad");
116         if (soundParsers_.size() >= MAX_LOAD_NUM) {
117             MEDIA_LOGI("SoundPool MAX_LOAD_NUM:%{public}zu.", MAX_LOAD_NUM);
118             return invalidSoundIDFlag;
119         }
120         do {
121             nextSoundID_ = nextSoundID_ == INT32_MAX ? 1 : nextSoundID_ + 1;
122         } while (FindSoundParser(nextSoundID_) != nullptr);
123         soundID = nextSoundID_;
124         auto soundParser = std::make_shared<SoundParser>(soundID, fd, offset, length);
125         CHECK_AND_RETURN_RET_LOG(soundParser != nullptr, -1, "failed to create soundParser");
126         soundParsers_.emplace(soundID, soundParser);
127     }
128     DoLoad(soundID);
129     return soundID;
130 }
131 
DoLoad(int32_t soundID)132 int32_t SoundIDManager::DoLoad(int32_t soundID)
133 {
134     MEDIA_LOGI("SoundIDManager::DoLoad soundID:%{public}d", soundID);
135     if (!isParsingThreadPoolStarted_) {
136         InitThreadPool();
137     }
138     {
139         std::unique_lock lock(soundManagerLock_);
140         while (soundIDs_.size() == MAX_SOUND_ID_QUEUE) {
141             if (quitQueue_) return MSERR_OK;
142             queueSpaceValid_.wait(lock);
143         }
144         if (quitQueue_) return MSERR_OK;
145         soundIDs_.push_back(soundID);
146         queueDataValid_.notify_one();
147     }
148     ThreadPool::Task soundParsingTask = [this] { this->DoParser(); };
149     CHECK_AND_RETURN_RET_LOG(soundParserThreadPool_ != nullptr, MSERR_INVALID_VAL, "Failed to obtain ThreadPool");
150     CHECK_AND_RETURN_RET_LOG(soundParsingTask != nullptr, MSERR_INVALID_VAL, "Failed to obtain Task");
151     soundParserThreadPool_->AddTask(soundParsingTask);
152     return MSERR_OK;
153 }
154 
DoParser()155 int32_t SoundIDManager::DoParser()
156 {
157     std::unique_lock lock(soundManagerLock_);
158     while (!quitQueue_) {
159         if (soundIDs_.empty()) {
160             queueDataValid_.wait_for(
161                 lock, std::chrono::duration<int32_t, std::milli>(WAIT_TIME_BEFORE_CLOSE_MS));
162             if (soundIDs_.empty()) {
163                 // no new sound, exit this thread.
164                 break;
165             }
166             continue;
167         }
168         const int32_t soundID = soundIDs_.front();
169         soundIDs_.pop_front();
170         queueSpaceValid_.notify_one();
171         std::shared_ptr<SoundParser> soundParser = FindSoundParser(soundID);
172         lock.unlock();
173         if (soundParser.get() != nullptr) {
174             soundParser->SetCallback(callback_);
175             soundParser->DoParser();
176         }
177         lock.lock();
178     }
179     return MSERR_OK;
180 }
181 
182 
FindSoundParser(int32_t soundID) const183 std::shared_ptr<SoundParser> SoundIDManager::FindSoundParser(int32_t soundID) const
184 {
185     MEDIA_LOGI("SoundIDManager::FindSoundParser soundID:%{public}d", soundID);
186     if (soundParsers_.empty()) {
187         return nullptr;
188     }
189     if (soundParsers_.find(soundID) != soundParsers_.end()) {
190         return soundParsers_.at(soundID);
191     }
192     return nullptr;
193 }
194 
Unload(int32_t soundID)195 int32_t SoundIDManager::Unload(int32_t soundID)
196 {
197     MEDIA_LOGI("SoundIDManager::Unload soundID:%{public}d", soundID);
198     std::unique_lock lock(soundManagerLock_);
199     CHECK_AND_RETURN_RET_LOG(!soundParsers_.empty(), MSERR_NO_MEMORY, "No sound in the soundParsers_");
200     auto it = soundParsers_.find(soundID);
201     if (it != soundParsers_.end()) {
202         if (it->second != nullptr) {
203             it->second.reset();
204         }
205         soundParsers_.erase(it);
206     } else {
207         MEDIA_LOGI("Invalid soundID, unload failed");
208         return MSERR_INVALID_VAL;
209     }
210     return MSERR_OK;
211 }
212 
SetCallback(const std::shared_ptr<ISoundPoolCallback> & callback)213 int32_t SoundIDManager::SetCallback(const std::shared_ptr<ISoundPoolCallback> &callback)
214 {
215     callback_ = callback;
216     return MSERR_OK;
217 }
218 } // namespace Media
219 } // namespace OHOS
220