• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 &parallelPlayFlag)
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