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