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