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 ¶llelPlayFlag)
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