1 /*
2  * Copyright (c) 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 #include "audio_haptic_sound_low_latency_impl.h"
17 
18 #include <fcntl.h>
19 
20 #include "isoundpool.h"
21 #include "audio_haptic_log.h"
22 #include "media_errors.h"
23 
24 namespace {
25 constexpr OHOS::HiviewDFX::HiLogLabel LABEL = {LOG_CORE, LOG_DOMAIN_AUDIO_NAPI, "AudioHapticSoundLowLatencyImpl"};
26 }
27 
28 namespace OHOS {
29 namespace Media {
30 const int32_t MAX_SOUND_POOL_STREAMS = 1; // ensure that only one stream for sound pool is playing.
31 const int32_t LOAD_WAIT_SECONDS = 2;
32 
AudioHapticSoundLowLatencyImpl(const std::string & audioUri,const bool & muteAudio,const AudioStandard::StreamUsage & streamUsage,const bool & parallelPlayFlag)33 AudioHapticSoundLowLatencyImpl::AudioHapticSoundLowLatencyImpl(const std::string &audioUri, const bool &muteAudio,
34     const AudioStandard::StreamUsage &streamUsage, const bool &parallelPlayFlag)
35     : audioUri_(audioUri),
36       muteAudio_(muteAudio),
37       parallelPlayFlag_(parallelPlayFlag),
38       streamUsage_(streamUsage)
39 {
40 }
41 
~AudioHapticSoundLowLatencyImpl()42 AudioHapticSoundLowLatencyImpl::~AudioHapticSoundLowLatencyImpl()
43 {
44     if (soundPoolPlayer_ != nullptr) {
45         ReleaseSoundPoolPlayer();
46     }
47 }
48 
LoadSoundPoolPlayer()49 int32_t AudioHapticSoundLowLatencyImpl::LoadSoundPoolPlayer()
50 {
51     MEDIA_LOGI("Enter LoadSoundPoolPlayer()");
52 
53     AudioStandard::AudioRendererInfo audioRendererInfo;
54     audioRendererInfo.contentType = AudioStandard::ContentType::CONTENT_TYPE_UNKNOWN;
55     audioRendererInfo.streamUsage = streamUsage_;
56     audioRendererInfo.rendererFlags = 1;
57 
58     soundPoolPlayer_ = SoundPoolFactory::CreateSoundPool(MAX_SOUND_POOL_STREAMS, audioRendererInfo);
59     CHECK_AND_RETURN_RET_LOG(soundPoolPlayer_ != nullptr, MSERR_INVALID_VAL,
60         "Failed to create sound pool player instance");
61 
62     soundPoolCallback_ = std::make_shared<AHSoundLowLatencyCallback>(shared_from_this());
63     CHECK_AND_RETURN_RET_LOG(soundPoolCallback_ != nullptr, MSERR_INVALID_VAL, "Failed to create callback object");
64     soundPoolPlayer_->SetSoundPoolCallback(soundPoolCallback_);
65 
66     firstFrameCallback_ = std::make_shared<AHSoundFirstFrameCallback>(shared_from_this());
67     CHECK_AND_RETURN_RET_LOG(firstFrameCallback_ != nullptr, MSERR_INVALID_VAL, "Failed to create callback object");
68     soundPoolPlayer_->SetSoundPoolFrameWriteCallback(firstFrameCallback_);
69 
70     configuredAudioUri_ = "";
71     playerState_ = AudioHapticPlayerState::STATE_NEW;
72     return MSERR_OK;
73 }
74 
OpenAudioUri(const std::string & audioUri)75 int32_t AudioHapticSoundLowLatencyImpl::OpenAudioUri(const std::string &audioUri)
76 {
77     if (fileDes_ != -1) {
78         (void)close(fileDes_);
79         fileDes_ = -1;
80     }
81 
82     const std::string fdHead = "fd://";
83     if (audioUri_.find(fdHead) != std::string::npos) {
84         int32_t fd = atoi(audioUri_.substr(fdHead.size()).c_str());
85         CHECK_AND_RETURN_RET_LOG(fd > 0, MSERR_OPEN_FILE_FAILED, "GetAudioUriFd: Failed to extract fd for avplayer.");
86         fileDes_ = dup(fd);
87         MEDIA_LOGI("fileDes_ == %{public}d", fileDes_);
88     } else {
89         char realPathRes[PATH_MAX + 1] = {'\0'};
90         if (audioUri.size() >= PATH_MAX || realpath(audioUri_.c_str(), realPathRes) == nullptr) {
91             MEDIA_LOGE("Invalid file path length");
92             return MSERR_UNSUPPORT_FILE;
93         }
94         std::string realPathStr(realPathRes);
95         fileDes_ = open(realPathStr.c_str(), O_RDONLY);
96         if (fileDes_ == -1) {
97             MEDIA_LOGE("GetAudioUriFd: Failed to open the audio uri for sound pool.");
98             return MSERR_OPEN_FILE_FAILED;
99         }
100     }
101     return fileDes_ != -1 ? MSERR_OK : MSERR_OPEN_FILE_FAILED;
102 }
103 
PrepareSound()104 int32_t AudioHapticSoundLowLatencyImpl::PrepareSound()
105 {
106     MEDIA_LOGI("Enter PrepareSound with sound pool");
107     std::lock_guard<std::mutex> lock(audioHapticPlayerLock_);
108     int32_t result = LoadSoundPoolPlayer();
109     CHECK_AND_RETURN_RET_LOG(result == MSERR_OK && soundPoolPlayer_ != nullptr, MSERR_INVALID_STATE,
110         "Audio haptic player(soundpool) instance is null");
111 
112     if (!configuredAudioUri_.empty() && configuredAudioUri_ == audioUri_) {
113         MEDIA_LOGI("Prepare: The audioUri_ uri has been loaded. Return directly.");
114         return MSERR_OK;
115     }
116 
117     MEDIA_LOGI("Set audio source to soundpool. audioUri [%{public}s]", audioUri_.c_str());
118     result = OpenAudioUri(audioUri_);
119     CHECK_AND_RETURN_RET_LOG(result == MSERR_OK, result, "Failed to get audio uri fd.");
120     std::string uri = "fd://" + std::to_string(fileDes_);
121 
122     int32_t soundID = soundPoolPlayer_->Load(uri);
123     if (soundID < 0) {
124         MEDIA_LOGE("Prepare: Failed to load soundPool uri.");
125         return MSERR_OPEN_FILE_FAILED;
126     }
127     std::unique_lock<std::mutex> lockPrepare(prepareMutex_);
128     prepareCond_.wait_for(lockPrepare, std::chrono::seconds(LOAD_WAIT_SECONDS),
129         [this]() { return isPrepared_ || isReleased_ || isUnsupportedFile_; });
130     CHECK_AND_RETURN_RET_LOG(!isReleased_, MSERR_INVALID_OPERATION, "The sound pool is released when it is preparing.");
131     CHECK_AND_RETURN_RET_LOG(!isUnsupportedFile_, MSERR_UNSUPPORT_FILE,
132         "Failed to load audio uri: report unsupported file err when loading source for sound pool.");
133     CHECK_AND_RETURN_RET_LOG(isPrepared_, MSERR_OPEN_FILE_FAILED, "Failed to load audio uri: time out.");
134 
135     // The audio source has been loaded for sound pool
136     soundID_ = soundID;
137     configuredAudioUri_ = audioUri_;
138     playerState_ = AudioHapticPlayerState::STATE_PREPARED;
139 
140     return MSERR_OK;
141 }
142 
StartSound()143 int32_t AudioHapticSoundLowLatencyImpl::StartSound()
144 {
145     MEDIA_LOGI("Enter StartSound with sound pool");
146     std::lock_guard<std::mutex> lock(audioHapticPlayerLock_);
147     if (playerState_ != AudioHapticPlayerState::STATE_PREPARED &&
148         playerState_ != AudioHapticPlayerState::STATE_RUNNING &&
149         playerState_ != AudioHapticPlayerState::STATE_STOPPED) {
150         MEDIA_LOGE("SoundPoolPlayer not Prepared");
151         return MSERR_START_FAILED;
152     }
153     CHECK_AND_RETURN_RET_LOG(soundPoolPlayer_ != nullptr, MSERR_INVALID_STATE, "Sound pool player instance is null");
154 
155     PlayParams playParams {
156         .loop = (loop_ ? -1 : 0),
157         .rate = 0, // default AudioRendererRate::RENDER_RATE_NORMAL
158         .leftVolume = volume_ * (muteAudio_ ? 0 : 1),
159         .rightVolume = volume_ * (muteAudio_ ? 0 : 1),
160         .priority = 0,
161         .parallelPlayFlag = parallelPlayFlag_,
162     };
163     streamID_ = soundPoolPlayer_->Play(soundID_, playParams);
164     playerState_ = AudioHapticPlayerState::STATE_RUNNING;
165 
166     return MSERR_OK;
167 }
168 
StopSound()169 int32_t AudioHapticSoundLowLatencyImpl::StopSound()
170 {
171     MEDIA_LOGI("Enter StopSound with sound pool");
172     std::lock_guard<std::mutex> lock(audioHapticPlayerLock_);
173     CHECK_AND_RETURN_RET_LOG(soundPoolPlayer_ != nullptr, MSERR_INVALID_STATE, "Sound pool player instance is null");
174 
175     (void)soundPoolPlayer_->Stop(streamID_);
176     playerState_ = AudioHapticPlayerState::STATE_STOPPED;
177 
178     return MSERR_OK;
179 }
180 
ReleaseSound()181 int32_t AudioHapticSoundLowLatencyImpl::ReleaseSound()
182 {
183     MEDIA_LOGI("Enter ReleaseSound with sound pool");
184     {
185         std::lock_guard<std::mutex> lockPrepare(prepareMutex_);
186         isReleased_ = true;
187         prepareCond_.notify_one();
188     }
189     std::lock_guard<std::mutex> lock(audioHapticPlayerLock_);
190     CHECK_AND_RETURN_RET_LOG(playerState_ != AudioHapticPlayerState::STATE_RELEASED, MSERR_OK,
191         "The audio haptic player has been released.");
192     ReleaseSoundPoolPlayer();
193     playerState_ = AudioHapticPlayerState::STATE_RELEASED;
194 
195     return MSERR_OK;
196 }
197 
ReleaseSoundPoolPlayer()198 void AudioHapticSoundLowLatencyImpl::ReleaseSoundPoolPlayer()
199 {
200     if (soundPoolPlayer_ != nullptr) {
201         (void)soundPoolPlayer_->Release();
202         soundPoolPlayer_ = nullptr;
203     }
204     soundPoolCallback_ = nullptr;
205     if (fileDes_ != -1) {
206         (void)close(fileDes_);
207         fileDes_ = -1;
208     }
209 }
210 
SetVolume(float volume)211 int32_t AudioHapticSoundLowLatencyImpl::SetVolume(float volume)
212 {
213     MEDIA_LOGI("AudioHapticSoundLowLatencyImpl::SetVolume %{public}f", volume);
214     if (volume < 0.0f || volume > 1.0f) {
215         MEDIA_LOGE("SetVolume: the volume value is invalid.");
216         return MSERR_INVALID_VAL;
217     }
218 
219     std::lock_guard<std::mutex> lock(audioHapticPlayerLock_);
220     int32_t result = MSERR_OK;
221     volume_ = volume;
222 
223     if (playerState_ != AudioHapticPlayerState::STATE_PREPARED &&
224         playerState_ != AudioHapticPlayerState::STATE_RUNNING) {
225         MEDIA_LOGI("Audio haptic player is not prepared or running. No need to modify player");
226         return result;
227     }
228     if (streamID_ != -1) {
229         float actualVolume = volume_ * (muteAudio_ ? 0 : 1);
230         result = soundPoolPlayer_->SetVolume(streamID_, actualVolume, actualVolume);
231     }
232     return result;
233 }
234 
SetLoop(bool loop)235 int32_t AudioHapticSoundLowLatencyImpl::SetLoop(bool loop)
236 {
237     MEDIA_LOGI("AudioHapticSoundLowLatencyImpl::SetLoop %{public}d", loop);
238     std::lock_guard<std::mutex> lock(audioHapticPlayerLock_);
239     int32_t result = MSERR_OK;
240     loop_ = loop;
241 
242     if (playerState_ != AudioHapticPlayerState::STATE_PREPARED &&
243         playerState_ != AudioHapticPlayerState::STATE_RUNNING) {
244         MEDIA_LOGI("Audio haptic player is not prepared or running. No need to modify player");
245         return result;
246     }
247     if (streamID_ != -1) {
248         int32_t loopCount = loop_ ? -1 : 0;
249         result = soundPoolPlayer_->SetLoop(streamID_, loopCount);
250     }
251     return result;
252 }
253 
GetAudioCurrentTime()254 int32_t AudioHapticSoundLowLatencyImpl::GetAudioCurrentTime()
255 {
256     MEDIA_LOGE("GetAudioCurrentTime is unsupported for sound pool.");
257     return -1;
258 }
259 
SetAudioHapticSoundCallback(const std::shared_ptr<AudioHapticSoundCallback> & callback)260 int32_t AudioHapticSoundLowLatencyImpl::SetAudioHapticSoundCallback(
261     const std::shared_ptr<AudioHapticSoundCallback> &callback)
262 {
263     if (callback == nullptr) {
264         MEDIA_LOGE("The audio haptic player callback is nullptr.");
265         return MSERR_INVALID_VAL;
266     }
267 
268     std::lock_guard<std::mutex> lock(audioHapticPlayerLock_);
269     audioHapticPlayerCallback_ = callback;
270     return MSERR_OK;
271 }
272 
NotifyPreparedEvent()273 void AudioHapticSoundLowLatencyImpl::NotifyPreparedEvent()
274 {
275     std::lock_guard<std::mutex> lockPrepare(prepareMutex_);
276     isPrepared_ = true;
277     prepareCond_.notify_one();
278 }
279 
NotifyErrorEvent(int32_t errorCode)280 void AudioHapticSoundLowLatencyImpl::NotifyErrorEvent(int32_t errorCode)
281 {
282     MediaServiceErrCode mediaErr = static_cast<MediaServiceErrCode>(errorCode);
283     if (mediaErr == MSERR_UNSUPPORT_FILE) {
284         std::lock_guard<std::mutex> lockPrepare(prepareMutex_);
285         isUnsupportedFile_ = true;
286         prepareCond_.notify_one();
287     }
288 
289     std::shared_ptr<AudioHapticSoundCallback> cb = audioHapticPlayerCallback_.lock();
290     if (cb != nullptr) {
291         MEDIA_LOGI("NotifyFirstFrameEvent for audio haptic player");
292         cb->OnError(errorCode);
293     } else {
294         MEDIA_LOGE("NotifyFirstFrameEvent: audioHapticPlayerCallback_ is nullptr");
295     }
296 }
297 
NotifyFirstFrameEvent(uint64_t latency)298 void AudioHapticSoundLowLatencyImpl::NotifyFirstFrameEvent(uint64_t latency)
299 {
300     std::shared_ptr<AudioHapticSoundCallback> cb = audioHapticPlayerCallback_.lock();
301     if (cb != nullptr) {
302         MEDIA_LOGI("NotifyFirstFrameEvent for audio haptic player");
303         cb->OnFirstFrameWriting(latency);
304     } else {
305         MEDIA_LOGE("NotifyFirstFrameEvent: audioHapticPlayerCallback_ is nullptr");
306     }
307 }
308 
NotifyEndOfStreamEvent()309 void AudioHapticSoundLowLatencyImpl::NotifyEndOfStreamEvent()
310 {
311     MEDIA_LOGI("NotifyEndOfStreamEvent");
312     playerState_ = AudioHapticPlayerState::STATE_STOPPED;
313     std::shared_ptr<AudioHapticSoundCallback> cb = audioHapticPlayerCallback_.lock();
314     if (cb != nullptr) {
315         MEDIA_LOGI("NotifyEndOfStreamEvent for audio haptic player");
316         cb->OnEndOfStream();
317     } else {
318         MEDIA_LOGE("NotifyEndOfStreamEvent: audioHapticPlayerCallback_ is nullptr");
319     }
320 }
321 
322 // Callback class symbols
AHSoundLowLatencyCallback(std::shared_ptr<AudioHapticSoundLowLatencyImpl> soundLowLatencyImpl)323 AHSoundLowLatencyCallback::AHSoundLowLatencyCallback(
324     std::shared_ptr<AudioHapticSoundLowLatencyImpl> soundLowLatencyImpl)
325     : soundLowLatencyImpl_(soundLowLatencyImpl) {}
326 
OnLoadCompleted(int32_t soundId)327 void AHSoundLowLatencyCallback::OnLoadCompleted(int32_t soundId)
328 {
329     MEDIA_LOGI("OnLoadCompleted reported from sound pool.");
330     std::shared_ptr<AudioHapticSoundLowLatencyImpl> soundLowLatencyImpl = soundLowLatencyImpl_.lock();
331     if (soundLowLatencyImpl == nullptr) {
332         MEDIA_LOGE("The audio haptic player for low latency mode has been released.");
333         return;
334     }
335     soundLowLatencyImpl->NotifyPreparedEvent();
336 }
337 
OnPlayFinished()338 void AHSoundLowLatencyCallback::OnPlayFinished()
339 {
340     MEDIA_LOGI("OnPlayFinished reported from sound pool.");
341     std::shared_ptr<AudioHapticSoundLowLatencyImpl> soundLowLatencyImpl = soundLowLatencyImpl_.lock();
342     if (soundLowLatencyImpl == nullptr) {
343         MEDIA_LOGE("The audio haptic player for low latency mode has been released.");
344         return;
345     }
346     soundLowLatencyImpl->NotifyEndOfStreamEvent();
347 }
348 
OnError(int32_t errorCode)349 void AHSoundLowLatencyCallback::OnError(int32_t errorCode)
350 {
351     MEDIA_LOGE("OnError reported from sound pool: %{public}d", errorCode);
352     std::shared_ptr<AudioHapticSoundLowLatencyImpl> soundLowLatencyImpl = soundLowLatencyImpl_.lock();
353     if (soundLowLatencyImpl == nullptr) {
354         MEDIA_LOGE("The audio haptic player for low latency mode has been released.");
355         return;
356     }
357     soundLowLatencyImpl->NotifyErrorEvent(errorCode);
358 }
359 
AHSoundFirstFrameCallback(std::shared_ptr<AudioHapticSoundLowLatencyImpl> soundLowLatencyImpl)360 AHSoundFirstFrameCallback::AHSoundFirstFrameCallback(
361     std::shared_ptr<AudioHapticSoundLowLatencyImpl> soundLowLatencyImpl)
362     : soundLowLatencyImpl_(soundLowLatencyImpl) {}
363 
OnFirstAudioFrameWritingCallback(uint64_t & latency)364 void AHSoundFirstFrameCallback::OnFirstAudioFrameWritingCallback(uint64_t &latency)
365 {
366     MEDIA_LOGI("OnFirstAudioFrameWritingCallback from Soundpool. Latency %{public}" PRIu64 "", latency);
367     std::shared_ptr<AudioHapticSoundLowLatencyImpl> soundLowLatencyImpl = soundLowLatencyImpl_.lock();
368     if (soundLowLatencyImpl == nullptr) {
369         MEDIA_LOGE("The audio haptic player for low latency mode has been released.");
370         return;
371     }
372     soundLowLatencyImpl->NotifyFirstFrameEvent(latency);
373 }
374 } // namesapce AudioStandard
375 } // namespace OHOS
376