diff --git a/frameworks/native/audiostream/include/fast_audio_stream.h b/frameworks/native/audiostream/include/fast_audio_stream.h new file mode 100644 index 0000000000000000000000000000000000000000..a7ae30643cd28e63e8e898282ed82094dff37fbf --- /dev/null +++ b/frameworks/native/audiostream/include/fast_audio_stream.h @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef FAST_AUDIO_STREAM_H +#define FAST_AUDIO_STREAM_H + +#include +#include +#include "timestamp.h" +#include "event_handler.h" +#include "event_runner.h" +#include "audio_info.h" + +#include "audio_process_in_client.h" +#include "audio_stream_tracker.h" +#include "i_audio_stream.h" + +namespace OHOS { +namespace AudioStandard { + +class FastAudioStreamCallback : public AudioDataCallback { +public: + FastAudioStreamCallback(const std::shared_ptr &procClient, + const std::shared_ptr &callback) + : procClient_(procClient), renderCallback_(callback) {}; + virtual ~FastAudioStreamCallback() = default; + + void OnHandleData(size_t length) override; + +private: + std::shared_ptr procClient_ = nullptr; + std::shared_ptr renderCallback_ = nullptr; +}; + +class FastAudioStream : public IAudioStream { +public: + FastAudioStream(AudioStreamType eStreamType, AudioMode eMode, int32_t appUid); + virtual ~FastAudioStream(); + + void SetClientID(int32_t clientPid, int32_t clientUid) override; + + void SetRendererInfo(const AudioRendererInfo &rendererInfo) override; + void SetCapturerInfo(const AudioCapturerInfo &capturerInfo) override; + int32_t SetAudioStreamInfo(const AudioStreamParams info, + const std::shared_ptr &proxyObj) override; + int32_t GetAudioStreamInfo(AudioStreamParams &info) override; + bool VerifyClientMicrophonePermission(uint32_t appTokenId, int32_t appUid, bool privacyFlag, + AudioPermissionState state) override; + bool getUsingPemissionFromPrivacy(const std::string &permissionName, uint32_t appTokenId, + AudioPermissionState state) override; + int32_t GetAudioSessionID(uint32_t &sessionID) override; + State GetState() override; + bool GetAudioTime(Timestamp ×tamp, Timestamp::Timestampbase base) override; + int32_t GetBufferSize(size_t &bufferSize) override; + int32_t GetFrameCount(uint32_t &frameCount) override; + int32_t GetLatency(uint64_t &latency) override; + int32_t SetAudioStreamType(AudioStreamType audioStreamType) override; + int32_t SetVolume(float volume) override; + float GetVolume() override; + int32_t SetRenderRate(AudioRendererRate renderRate) override; + AudioRendererRate GetRenderRate() override; + int32_t SetStreamCallback(const std::shared_ptr &callback) override; + + // callback mode api + int32_t SetRenderMode(AudioRenderMode renderMode) override; + AudioRenderMode GetRenderMode() override; + int32_t SetRendererWriteCallback(const std::shared_ptr &callback) override; + int32_t SetCaptureMode(AudioCaptureMode captureMode) override; + AudioCaptureMode GetCaptureMode() override; + int32_t SetCapturerReadCallback(const std::shared_ptr &callback) override; + int32_t GetBufferDesc(BufferDesc &bufDesc) override; + int32_t GetBufQueueState(BufferQueueState &bufState) override; + int32_t Enqueue(const BufferDesc &bufDesc) override; + int32_t Clear() override; + + int32_t SetLowPowerVolume(float volume) override; + float GetLowPowerVolume() override; + float GetSingleStreamVolume() override; + AudioEffectMode GetAudioEffectMode() override; + int32_t SetAudioEffectMode(AudioEffectMode effectMode) override; + int64_t GetFramesWritten() override; + int64_t GetFramesRead() override; + + void SetInnerCapturerState(bool isInnerCapturer) override; + void SetPrivacyType(AudioPrivacyType privacyType) override; + + // Common APIs + bool StartAudioStream(StateChangeCmdType cmdType = CMD_FROM_CLIENT) override; + bool PauseAudioStream(StateChangeCmdType cmdType = CMD_FROM_CLIENT) override; + bool StopAudioStream() override; + bool ReleaseAudioStream(bool releaseRunner = true) override; + bool FlushAudioStream() override; + + // Playback related APIs + bool DrainAudioStream() override; + size_t Write(uint8_t *buffer, size_t buffer_size) override; + + // Recording related APIs + int32_t Read(uint8_t &buffer, size_t userSize, bool isBlockingRead) override; + + uint32_t GetUnderflowCount() override; + void SetRendererPositionCallback(int64_t markPosition, const std::shared_ptr &callback) + override; + void UnsetRendererPositionCallback() override; + void SetRendererPeriodPositionCallback(int64_t markPosition, + const std::shared_ptr &callback) override; + void UnsetRendererPeriodPositionCallback() override; + void SetCapturerPositionCallback(int64_t markPosition, const std::shared_ptr &callback) + override; + void UnsetCapturerPositionCallback() override; + void SetCapturerPeriodPositionCallback(int64_t markPosition, + const std::shared_ptr &callback) override; + void UnsetCapturerPeriodPositionCallback() override; + int32_t SetRendererSamplingRate(uint32_t sampleRate) override; + uint32_t GetRendererSamplingRate() override; + int32_t SetBufferSizeInMsec(int32_t bufferSizeInMsec) override; + void SetApplicationCachePath(const std::string cachePath) override; + +private: + AudioStreamType eStreamType_; + AudioMode eMode_; + std::shared_ptr spkProcessClient_ = nullptr; + std::shared_ptr spkProcClientCb_ = nullptr; + std::unique_ptr audioStreamTracker_; + AudioRendererInfo rendererInfo_; + AudioCapturerInfo capturerInfo_; + AudioStreamParams streamInfo_; + AudioProcessConfig processconfig_; + State state_; + uint32_t sessionId_; + std::string cachePath_ = ""; + uint32_t underflowCount_; + AudioRenderMode renderMode_; + AudioCaptureMode captureMode_; + AudioRendererRate renderRate_ = RENDER_RATE_NORMAL; + int32_t clientPid_ = 0; + int32_t clientUid_ = 0; +}; +} // namespace AudioStandard +} // namespace OHOS +#endif // FAST_AUDIO_STREAM_H diff --git a/services/audio_service/BUILD.gn b/services/audio_service/BUILD.gn index 97d109a738754e4275e595f030a8905f323e3d68..7b2a394ae395e786c9209a376b3f690bf01c11bd 100644 --- a/services/audio_service/BUILD.gn +++ b/services/audio_service/BUILD.gn @@ -116,6 +116,7 @@ ohos_shared_library("audio_client") { "client/src/audio_stream_manager.cpp", "client/src/audio_stream_tracker.cpp", "client/src/audio_system_manager.cpp", + "client/src/fast_audio_stream.cpp", "client/src/i_audio_stream.cpp", "client/src/policy_provider_stub.cpp", ] @@ -403,6 +404,39 @@ ohos_executable("audio_service_playback_test") { subsystem_name = "multimedia" } +ohos_executable("audio_faststream_playback_test") { + install_enable = false + + sources = [ "test/example/fast_audio_stream_playback_test.cpp" ] + + configs = [ ":audio_client_public_config" ] + + deps = [ + ":audio_client", + "../../frameworks/native/audiorenderer:audio_renderer", + ] + + include_dirs = [ + "../../../../foundation/communication/ipc/interfaces/innerkits/ipc_core/include", + "../../../../utils/system/safwk/native/include", + "../../../../commonlibrary/c_utils/base/include", + "$pulseaudio_dir/src", + "$pulseaudio_dir/confgure/src", + "$pulseaudio_dir/include", + "$pulseaudio_build_path/include", + "//third_party/bounds_checking_function/include", + ] + + external_deps = [ + "hilog:libhilog", + "init:libbegetutil", + "ipc:ipc_single", + ] + + part_name = "audio_framework" + subsystem_name = "multimedia" +} + ohos_executable("audio_service_record_test") { install_enable = false diff --git a/services/audio_service/client/include/audio_process_in_client.h b/services/audio_service/client/include/audio_process_in_client.h index 4efa2ce6014d93fcb9edd76a9c40c4a3c8542231..cbabb0d0dd183f7ae3ab74e106826391887c9820 100644 --- a/services/audio_service/client/include/audio_process_in_client.h +++ b/services/audio_service/client/include/audio_process_in_client.h @@ -49,6 +49,7 @@ class ClientUnderrunCallBack { class AudioProcessInClient { public: static constexpr int32_t PROCESS_VOLUME_MAX = 1 << 16; // 0 ~ 65536 + static bool CheckIfSupport(const AudioProcessConfig &config); static std::shared_ptr Create(const AudioProcessConfig &config); virtual ~AudioProcessInClient() = default; @@ -88,6 +89,8 @@ public: virtual float GetVolume() = 0; + virtual uint32_t GetUnderflowCount() = 0; + virtual int64_t GetFramesWritten() = 0; virtual int64_t GetFramesRead() = 0; diff --git a/services/audio_service/client/src/audio_process_in_client.cpp b/services/audio_service/client/src/audio_process_in_client.cpp index c8fe9f69e4a9de733a3c2740f445b8e62d387378..1ce1d61984bc8322b287894392173ac2b2a31408 100644 --- a/services/audio_service/client/src/audio_process_in_client.cpp +++ b/services/audio_service/client/src/audio_process_in_client.cpp @@ -79,6 +79,8 @@ public: float GetVolume() override; + uint32_t GetUnderflowCount() override; + int64_t GetFramesWritten() override; int64_t GetFramesRead() override; @@ -89,7 +91,6 @@ public: static const sptr GetAudioServerProxy(); static void AudioServerDied(pid_t pid); - static bool CheckIfSupport(const AudioProcessConfig &config); static constexpr AudioStreamInfo g_targetStreamInfo = {SAMPLE_RATE_48000, ENCODING_PCM, SAMPLE_S16LE, STEREO}; private: @@ -159,6 +160,7 @@ private: std::mutex loopThreadLock_; std::condition_variable threadStatusCV_; + std::atomic underflowCount_ = 0; std::string cachePath_; #ifdef DUMP_CLIENT FILE *dcp_ = nullptr; @@ -223,7 +225,7 @@ std::shared_ptr AudioProcessInClient::Create(const AudioPr { AUDIO_INFO_LOG("Create with config: render flag %{public}d, capturer flag %{public}d, isRemote %{public}d.", config.rendererInfo.rendererFlags, config.capturerInfo.capturerFlags, config.isRemote); - if (config.audioMode == AUDIO_MODE_PLAYBACK && !AudioProcessInClientInner::CheckIfSupport(config)) { + if (config.audioMode == AUDIO_MODE_PLAYBACK && !AudioProcessInClient::CheckIfSupport(config)) { AUDIO_ERR_LOG("CheckIfSupport failed!"); return nullptr; } @@ -270,7 +272,12 @@ AudioProcessInClientInner::~AudioProcessInClientInner() int32_t AudioProcessInClientInner::GetSessionID(uint32_t &sessionID) { // note: Get the session id from server. - sessionID = 0; // 0 for debug + int32_t pid = processConfig_.appInfo.appUid; + if (pid < 0) { + AUDIO_ERR_LOG("GetSessionID failed:%{public}d", pid); + return ERR_OPERATION_FAILED; + } + sessionID = static_cast(pid); // using pid as sessionID temporarily return SUCCESS; } @@ -332,6 +339,11 @@ float AudioProcessInClientInner::GetVolume() return volumeInFloat_; } +uint32_t AudioProcessInClientInner::GetUnderflowCount() +{ + return underflowCount_.load(); +} + int64_t AudioProcessInClientInner::GetFramesWritten() { if (processConfig_.audioMode != AUDIO_MODE_PLAYBACK) { @@ -551,7 +563,7 @@ int32_t AudioProcessInClientInner::GetBufferDesc(BufferDesc &bufDesc) const return SUCCESS; } -bool AudioProcessInClientInner::CheckIfSupport(const AudioProcessConfig &config) +bool AudioProcessInClient::CheckIfSupport(const AudioProcessConfig &config) { if (config.streamInfo.encoding != ENCODING_PCM || config.streamInfo.samplingRate != SAMPLE_RATE_48000) { return false; @@ -1180,6 +1192,7 @@ bool AudioProcessInClientInner::FinishHandleCurrent(uint64_t &curWritePos, int64 clientWriteCost = tempSpan->writeDoneTime - tempSpan->writeStartTime; if (clientWriteCost > MAX_WRITE_COST_DURATION_NANO) { AUDIO_WARNING_LOG("Client write cost too long..."); + underflowCount_++; // todo // handle write time out: send underrun msg to client, reset time model with latest server handle time. } diff --git a/services/audio_service/client/src/fast_audio_stream.cpp b/services/audio_service/client/src/fast_audio_stream.cpp new file mode 100644 index 0000000000000000000000000000000000000000..507eab8da28d0b11aa6ad671ecbbf0ac44afb713 --- /dev/null +++ b/services/audio_service/client/src/fast_audio_stream.cpp @@ -0,0 +1,586 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +#include "audio_errors.h" +#include "audio_log.h" +#include "audio_utils.h" + +#include "fast_audio_stream.h" + +using namespace std; + +namespace OHOS { +namespace AudioStandard { +FastAudioStream::FastAudioStream(AudioStreamType eStreamType, AudioMode eMode, int32_t appUid) + : eStreamType_(eStreamType), + eMode_(eMode), + state_(NEW), + renderMode_(RENDER_MODE_CALLBACK), + captureMode_(CAPTURE_MODE_CALLBACK) +{ + AUDIO_INFO_LOG("FastAudioStream ctor, appUID = %{public}d", appUid); + audioStreamTracker_ = std::make_unique(eMode, appUid); + AUDIO_DEBUG_LOG("AudioStreamTracker created"); +} + +FastAudioStream::~FastAudioStream() +{ + if (state_ != RELEASED && state_ != NEW) { + ReleaseAudioStream(false); + } +} + +void FastAudioStream::SetClientID(int32_t clientPid, int32_t clientUid) +{ + AUDIO_INFO_LOG("Set client PID: %{public}d, UID: %{public}d", clientPid, clientUid); + clientPid_ = clientPid; + clientUid_ = clientUid; +} + +void FastAudioStream::SetRendererInfo(const AudioRendererInfo &rendererInfo) +{ + rendererInfo_ = rendererInfo; +} + +void FastAudioStream::SetCapturerInfo(const AudioCapturerInfo &capturerInfo) +{ + capturerInfo_ = capturerInfo; +} + +int32_t FastAudioStream::SetAudioStreamInfo(const AudioStreamParams info, + const std::shared_ptr &proxyObj) +{ + AUDIO_INFO_LOG("FastAudioStreamInfo, Sampling rate: %{public}d, channels: %{public}d, format: %{public}d," + " stream type: %{public}d", info.samplingRate, info.channels, info.format, eStreamType_); + if (spkProcessClient_ != nullptr) { + AUDIO_ERR_LOG("Process is already inited, reset stream info is not supported."); + return ERR_INVALID_OPERATION; + } + streamInfo_ = info; + if (state_ != NEW) { + AUDIO_INFO_LOG("FastAudioStream: State is not new, release existing stream"); + StopAudioStream(); + ReleaseAudioStream(false); + } + if (eMode_ == AUDIO_MODE_PLAYBACK) { + AUDIO_DEBUG_LOG("FastAudioStream: Initialize playback"); + AudioProcessConfig config; + config.appInfo.appPid = clientPid_; + config.appInfo.appUid = clientUid_; + config.audioMode = eMode_; + config.rendererInfo.contentType = rendererInfo_.contentType; + config.rendererInfo.streamUsage = rendererInfo_.streamUsage; + config.rendererInfo.rendererFlags = STREAM_FLAG_FAST; + config.streamInfo.channels = static_cast(info.channels); + config.streamInfo.encoding = static_cast(info.encoding); + config.streamInfo.format = static_cast(info.format); + config.streamInfo.samplingRate = static_cast(info.samplingRate); + config.isRemote = false; // note: + CHECK_AND_RETURN_RET_LOG(AudioProcessInClient::CheckIfSupport(config), ERR_INVALID_PARAM, + "Stream is not supported."); + processconfig_ = config; + spkProcessClient_ = AudioProcessInClient::Create(config); + CHECK_AND_RETURN_RET_LOG(spkProcessClient_ != nullptr, ERR_INVALID_PARAM, + "Client test creat process client fail."); + } else if (eMode_ == AUDIO_MODE_RECORD) { + AUDIO_DEBUG_LOG("FastAudioStream: Initialize recording"); + } else { + AUDIO_ERR_LOG("FastAudioStream: error eMode."); + return ERR_INVALID_OPERATION; + } + + state_ = PREPARED; + if (audioStreamTracker_ != nullptr && audioStreamTracker_.get()) { + spkProcessClient_->GetSessionID(sessionId_); + AUDIO_DEBUG_LOG("AudioStream:Calling register tracker, sessionid = %{public}d", sessionId_); + audioStreamTracker_->RegisterTracker(sessionId_, state_, rendererInfo_, capturerInfo_, proxyObj); + } + return SUCCESS; +} + +int32_t FastAudioStream::GetAudioStreamInfo(AudioStreamParams &audioStreamInfo) +{ + AUDIO_INFO_LOG("GetAudioStreamInfo in"); + audioStreamInfo = streamInfo_; + return SUCCESS; +} + +bool FastAudioStream::VerifyClientMicrophonePermission(uint32_t appTokenId, int32_t appUid, bool privacyFlag, + AudioPermissionState state) +{ + AUDIO_INFO_LOG("VerifyClientPermission in"); + // note: add support later + return true; +} + +bool FastAudioStream::getUsingPemissionFromPrivacy(const std::string &permissionName, uint32_t appTokenId, + AudioPermissionState state) +{ + AUDIO_INFO_LOG("getUsingPemissionFromPrivacy in"); + // note: add support later + return true; +} + +int32_t FastAudioStream::GetAudioSessionID(uint32_t &sessionID) +{ + CHECK_AND_RETURN_RET_LOG(spkProcessClient_ != nullptr, ERR_OPERATION_FAILED, + "GetAudioSessionID failed: null process"); + int32_t ret = spkProcessClient_->GetSessionID(sessionID); + CHECK_AND_RETURN_RET_LOG(ret == SUCCESS, ret, "GetSessionID error."); + return ret; +} + +State FastAudioStream::GetState() +{ + return state_; +} + +bool FastAudioStream::GetAudioTime(Timestamp ×tamp, Timestamp::Timestampbase base) +{ + CHECK_AND_RETURN_RET_LOG(base == Timestamp::MONOTONIC, false, "GetAudioTime failed: invalid base"); + + CHECK_AND_RETURN_RET_LOG(spkProcessClient_ != nullptr, false, "GetAudioTime failed: null process"); + int64_t timeSec = 0; + int64_t timeNsec = 0; + int32_t ret = spkProcessClient_->GetAudioTime(timestamp.framePosition, timeSec, timeNsec); + CHECK_AND_RETURN_RET_LOG(ret == SUCCESS, false, "GetBufferSize error."); + timestamp.time.tv_sec = timeSec; + timestamp.time.tv_nsec = timeNsec; + return true; +} + +int32_t FastAudioStream::GetBufferSize(size_t &bufferSize) +{ + CHECK_AND_RETURN_RET_LOG(spkProcessClient_ != nullptr, ERR_OPERATION_FAILED, "GetBufferSize failed: null process"); + int32_t ret = spkProcessClient_->GetBufferSize(bufferSize); + CHECK_AND_RETURN_RET_LOG(ret == SUCCESS, ret, "GetBufferSize error."); + return ret; +} + +int32_t FastAudioStream::GetFrameCount(uint32_t &frameCount) +{ + CHECK_AND_RETURN_RET_LOG(spkProcessClient_ != nullptr, ERR_OPERATION_FAILED, "GetFrameCount failed: null process"); + int32_t ret = spkProcessClient_->GetFrameCount(frameCount); + CHECK_AND_RETURN_RET_LOG(ret == SUCCESS, ret, "GetFrameCount error."); + return ret; +} + +int32_t FastAudioStream::GetLatency(uint64_t &latency) +{ + CHECK_AND_RETURN_RET_LOG(spkProcessClient_ != nullptr, ERR_OPERATION_FAILED, "GetLatency failed: null process"); + int32_t ret = spkProcessClient_->GetLatency(latency); + CHECK_AND_RETURN_RET_LOG(ret == SUCCESS, ret, "GetLatency error."); + return ret; +} + +int32_t FastAudioStream::SetAudioStreamType(AudioStreamType audioStreamType) +{ + // Stream type can only be set when create. + AUDIO_ERR_LOG("Unsupported operation: SetAudioStreamType"); + return ERR_INVALID_OPERATION; +} + +int32_t FastAudioStream::SetVolume(float volume) +{ + CHECK_AND_RETURN_RET_LOG(spkProcessClient_ != nullptr, ERR_OPERATION_FAILED, "SetVolume failed: null process"); + int32_t ret = spkProcessClient_->SetVolume(volume); + CHECK_AND_RETURN_RET_LOG(ret == SUCCESS, ret, "SetVolume error."); + return ret; +} + +float FastAudioStream::GetVolume() +{ + CHECK_AND_RETURN_RET_LOG(spkProcessClient_ != nullptr, 1.0f, "SetVolume failed: null process"); // 1.0f for default + return spkProcessClient_->GetVolume(); +} + +int32_t FastAudioStream::SetRenderRate(AudioRendererRate renderRate) +{ + if (RENDER_RATE_NORMAL == renderRate) { + return SUCCESS; + } + AUDIO_ERR_LOG("Unsupported operation: SetRenderRate"); + return ERR_INVALID_OPERATION; +} + +AudioRendererRate FastAudioStream::GetRenderRate() +{ + return renderRate_; +} + +int32_t FastAudioStream::SetStreamCallback(const std::shared_ptr &callback) +{ + AUDIO_INFO_LOG("SetStreamCallback in"); + // note: need add support + return SUCCESS; +} + +int32_t FastAudioStream::SetRenderMode(AudioRenderMode renderMode) +{ + if (renderMode != RENDER_MODE_CALLBACK || eMode_ != AUDIO_MODE_PLAYBACK) { + AUDIO_ERR_LOG("SetRenderMode is not supported."); + return ERR_INVALID_OPERATION; + } + return SUCCESS; +} + +AudioRenderMode FastAudioStream::GetRenderMode() +{ + AUDIO_INFO_LOG("GetRenderMode in"); + return renderMode_; +} + +int32_t FastAudioStream::SetRendererWriteCallback(const std::shared_ptr &callback) +{ + AUDIO_INFO_LOG("SetRendererWriteCallback in."); + if (!callback || !spkProcessClient_) { + AUDIO_ERR_LOG("SetRendererWriteCallback callback is nullptr"); + return ERR_INVALID_PARAM; + } + spkProcClientCb_ = std::make_shared(spkProcessClient_, callback); + int32_t ret = spkProcessClient_->SaveDataCallback(spkProcClientCb_); + CHECK_AND_RETURN_RET_LOG(ret == SUCCESS, ret, "Client test save data callback fail, ret %{public}d.", ret); + return SUCCESS; +} + +int32_t FastAudioStream::SetCaptureMode(AudioCaptureMode captureMode) +{ + if (captureMode != CAPTURE_MODE_CALLBACK || eMode_ != AUDIO_MODE_RECORD) { + AUDIO_ERR_LOG("SetCaptureMode is not supported."); + return ERR_INVALID_OPERATION; + } + return SUCCESS; +} + +AudioCaptureMode FastAudioStream::GetCaptureMode() +{ + return captureMode_; +} + +int32_t FastAudioStream::SetCapturerReadCallback(const std::shared_ptr &callback) +{ + AUDIO_INFO_LOG("SetCapturerReadCallback in."); + // note: need add support + return SUCCESS; +} + +int32_t FastAudioStream::GetBufferDesc(BufferDesc &bufDesc) +{ + AUDIO_INFO_LOG("GetBufferDesc in."); + if (!spkProcessClient_) { + AUDIO_ERR_LOG("spkClient is null."); + } + int32_t ret = spkProcessClient_->GetBufferDesc(bufDesc); + if (ret != SUCCESS || bufDesc.buffer == nullptr || bufDesc.bufLength ==0) { + AUDIO_ERR_LOG("GetBufferDesc failed."); + return -1; + } + return SUCCESS; +} + +int32_t FastAudioStream::GetBufQueueState(BufferQueueState &bufState) +{ + AUDIO_INFO_LOG("GetBufQueueState in."); + // note: add support + return SUCCESS; +} + +int32_t FastAudioStream::Enqueue(const BufferDesc &bufDesc) +{ + AUDIO_INFO_LOG("Enqueue in"); + if (!spkProcessClient_) { + AUDIO_ERR_LOG("spkClient is null."); + } + int32_t ret = spkProcessClient_->Enqueue(bufDesc); + if (ret != SUCCESS) { + AUDIO_ERR_LOG("Enqueue failed."); + return -1; + } + return SUCCESS; +} + +int32_t FastAudioStream::Clear() +{ + AUDIO_INFO_LOG("Clear will do nothing."); + + return SUCCESS; +} + +int32_t FastAudioStream::SetLowPowerVolume(float volume) +{ + AUDIO_INFO_LOG("SetLowPowerVolume in."); + return 1.0f; +} + +float FastAudioStream::GetLowPowerVolume() +{ + AUDIO_INFO_LOG("GetLowPowerVolume in."); + return 1.0f; +} + +float FastAudioStream::GetSingleStreamVolume() +{ + AUDIO_INFO_LOG("GetSingleStreamVolume in."); + return 1.0f; +} + +AudioEffectMode FastAudioStream::GetAudioEffectMode() +{ + AUDIO_ERR_LOG("GetAudioEffectMode not supported"); + return EFFECT_NONE; +} + +int32_t FastAudioStream::SetAudioEffectMode(AudioEffectMode effectMode) +{ + AUDIO_ERR_LOG("SetAudioEffectMode not supported"); + return ERR_NOT_SUPPORTED; +} + +int64_t FastAudioStream::GetFramesWritten() +{ + int64_t result = -1; // -1 invalid frame + CHECK_AND_RETURN_RET_LOG(spkProcessClient_ != nullptr, result, "GetFramesWritten failed: null process"); + result = spkProcessClient_->GetFramesWritten(); + return result; +} + +int64_t FastAudioStream::GetFramesRead() +{ + int64_t result = -1; // -1 invalid frame + CHECK_AND_RETURN_RET_LOG(spkProcessClient_ != nullptr, result, "GetFramesRead failed: null process"); + result = spkProcessClient_->GetFramesRead(); + return result; +} + +bool FastAudioStream::StartAudioStream(StateChangeCmdType cmdType) +{ + AUDIO_INFO_LOG("StartAudioStream in."); + if ((state_ != PREPARED) && (state_ != STOPPED) && (state_ != PAUSED)) { + AUDIO_ERR_LOG("StartAudioStream Illegal state:%{public}u", state_); + return false; + } + + CHECK_AND_RETURN_RET_LOG(spkProcessClient_ != nullptr, false, "Start failed, process is null."); + int32_t ret = spkProcessClient_->Start(); + CHECK_AND_RETURN_RET_LOG(ret == SUCCESS, false, "Client test stop fail, ret %{public}d.", ret); + state_ = RUNNING; + + AUDIO_INFO_LOG("StartAudioStream SUCCESS, sessionId: %{public}d", sessionId_); + + if (audioStreamTracker_ != nullptr && audioStreamTracker_.get()) { + AUDIO_DEBUG_LOG("AudioStream:Calling Update tracker for Running"); + audioStreamTracker_->UpdateTracker(sessionId_, state_, rendererInfo_, capturerInfo_); + } + return true; +} + +bool FastAudioStream::PauseAudioStream(StateChangeCmdType cmdType) +{ + AUDIO_INFO_LOG("PauseAudioStream in"); + if (state_ != RUNNING) { + AUDIO_ERR_LOG("PauseAudioStream: State is not RUNNING. Illegal state:%{public}u", state_); + return false; + } + State oldState = state_; + + state_ = PAUSED; + CHECK_AND_RETURN_RET_LOG(spkProcessClient_ != nullptr, false, "Pause failed, process is null."); + int32_t ret = spkProcessClient_->Pause(); + if (ret != SUCCESS) { + AUDIO_ERR_LOG("StreamPause fail,ret:%{public}d", ret); + state_ = oldState; + return false; + } + + AUDIO_INFO_LOG("PauseAudioStream SUCCESS, sessionId: %{public}d", sessionId_); + if (audioStreamTracker_ != nullptr && audioStreamTracker_.get()) { + AUDIO_DEBUG_LOG("AudioStream:Calling Update tracker for Pause"); + audioStreamTracker_->UpdateTracker(sessionId_, state_, rendererInfo_, capturerInfo_); + } + return true; +} + +bool FastAudioStream::StopAudioStream() +{ + if ((state_ != RUNNING) && (state_ != PAUSED)) { + AUDIO_ERR_LOG("StopAudioStream: State is not RUNNING. Illegal state:%{public}u", state_); + return false; + } + State oldState = state_; + state_ = STOPPED; // Set it before stopping as Read/Write and Stop can be called from different threads + + CHECK_AND_RETURN_RET_LOG(spkProcessClient_ != nullptr, false, "Stop failed, process is null."); + int32_t ret = spkProcessClient_->Stop(); + if (ret != SUCCESS) { + AUDIO_ERR_LOG("StreamStop fail,ret:%{public}d", ret); + state_ = oldState; + return false; + } + + AUDIO_INFO_LOG("StopAudioStream SUCCESS, sessionId: %{public}d", sessionId_); + if (audioStreamTracker_ != nullptr && audioStreamTracker_.get()) { + AUDIO_DEBUG_LOG("AudioStream:Calling Update tracker for stop"); + audioStreamTracker_->UpdateTracker(sessionId_, state_, rendererInfo_, capturerInfo_); + } + return true; +} + +bool FastAudioStream::FlushAudioStream() +{ + AUDIO_INFO_LOG("FlushAudioStream in."); + return true; +} + +bool FastAudioStream::DrainAudioStream() +{ + AUDIO_INFO_LOG("Drain stream SUCCESS"); + return true; +} + +bool FastAudioStream::ReleaseAudioStream(bool releaseRunner) +{ + if (state_ == RELEASED || state_ == NEW) { + AUDIO_ERR_LOG("Illegal state: state = %{public}u", state_); + return false; + } + // If state_ is RUNNING try to Stop it first and Release + if (state_ == RUNNING) { + StopAudioStream(); + } + + CHECK_AND_RETURN_RET_LOG(spkProcessClient_ != nullptr, false, "Release failed, process is null."); + spkProcessClient_->Release(); + state_ = RELEASED; + AUDIO_INFO_LOG("ReleaseAudiostream SUCCESS, sessionId: %{public}d", sessionId_); + if (audioStreamTracker_ != nullptr && audioStreamTracker_.get()) { + AUDIO_DEBUG_LOG("AudioStream:Calling Update tracker for release"); + audioStreamTracker_->UpdateTracker(sessionId_, state_, rendererInfo_, capturerInfo_); + } + return true; +} + +int32_t FastAudioStream::Read(uint8_t &buffer, size_t userSize, bool isBlockingRead) +{ + AUDIO_ERR_LOG("Unsupported operation: read"); + return ERR_INVALID_OPERATION; +} + +size_t FastAudioStream::Write(uint8_t *buffer, size_t buffer_size) +{ + AUDIO_ERR_LOG("Unsupported operation: Write"); + return ERR_INVALID_OPERATION; +} + +uint32_t FastAudioStream::GetUnderflowCount() +{ + AUDIO_INFO_LOG("GetUnderflowCount in."); + CHECK_AND_RETURN_RET_LOG(spkProcessClient_ != nullptr, 0, "process client is null."); + underflowCount_ = spkProcessClient_->GetUnderflowCount(); + return underflowCount_; +} + +void FastAudioStream::SetRendererPositionCallback(int64_t markPosition, + const std::shared_ptr &callback) +{ + AUDIO_INFO_LOG("Registering render frame position callback mark position"); + // note: need support +} + +void FastAudioStream::UnsetRendererPositionCallback() +{ + AUDIO_INFO_LOG("Unregistering render frame position callback"); + // note: need support +} + +void FastAudioStream::SetRendererPeriodPositionCallback(int64_t periodPosition, + const std::shared_ptr &callback) +{ + AUDIO_INFO_LOG("Registering render period position callback"); +} + +void FastAudioStream::UnsetRendererPeriodPositionCallback() +{ + AUDIO_INFO_LOG("Unregistering render period position callback"); +} + +void FastAudioStream::SetCapturerPositionCallback(int64_t markPosition, + const std::shared_ptr &callback) +{ + AUDIO_INFO_LOG("Registering capture frame position callback, mark position"); +} + +void FastAudioStream::UnsetCapturerPositionCallback() +{ + AUDIO_INFO_LOG("Unregistering capture frame position callback"); +} + +void FastAudioStream::SetCapturerPeriodPositionCallback(int64_t periodPosition, + const std::shared_ptr &callback) +{ + AUDIO_INFO_LOG("Registering period position callback"); +} + +void FastAudioStream::UnsetCapturerPeriodPositionCallback() +{ + AUDIO_INFO_LOG("Unregistering period position callback"); +} + +int32_t FastAudioStream::SetRendererSamplingRate(uint32_t sampleRate) +{ + AUDIO_ERR_LOG("SetRendererSamplingRate is not supported"); + + return ERR_OPERATION_FAILED; +} + +uint32_t FastAudioStream::GetRendererSamplingRate() +{ + AUDIO_INFO_LOG("GetRendererSamplingRate in"); + return streamInfo_.samplingRate; +} + +int32_t FastAudioStream::SetBufferSizeInMsec(int32_t bufferSizeInMsec) +{ + AUDIO_ERR_LOG("SetBufferSizeInMsec is not supported"); + // note: add support + return ERR_NOT_SUPPORTED; +} + +void FastAudioStream::SetApplicationCachePath(const std::string cachePath) +{ + AUDIO_INFO_LOG("SetApplicationCachePath to %{public}s", cachePath.c_str()); + + cachePath_ = cachePath; +} +void FastAudioStream::SetInnerCapturerState(bool isInnerCapturer) +{ + AUDIO_ERR_LOG("SetInnerCapturerState is not supported"); +} + +void FastAudioStream::SetPrivacyType(AudioPrivacyType privacyType) +{ + AUDIO_ERR_LOG("SetPrivacyType is not supported"); +} + +void FastAudioStreamCallback::OnHandleData(size_t length) +{ + CHECK_AND_RETURN_LOG(renderCallback_!= nullptr, "OnHandleData failed: renderCallback_ is null."); + renderCallback_->OnWriteData(length); +} +} // namespace AudioStandard +} // namespace OHOS diff --git a/services/audio_service/client/src/i_audio_stream.cpp b/services/audio_service/client/src/i_audio_stream.cpp index 1f77b05c3b93296f3c9c493c65ecd189b17ecbc6..9923d066b81bd95b61a0233ef2fed5c5b030c438 100644 --- a/services/audio_service/client/src/i_audio_stream.cpp +++ b/services/audio_service/client/src/i_audio_stream.cpp @@ -18,6 +18,7 @@ #include "audio_log.h" #include "audio_stream.h" +#include "fast_audio_stream.h" namespace OHOS { namespace AudioStandard { @@ -130,6 +131,7 @@ std::shared_ptr IAudioStream::GetPlaybackStream(StreamClass stream (void)params; // todo: use params eStreamType uid to create fast stream AUDIO_INFO_LOG("Create fast stream"); + return std::make_shared(eStreamType, AUDIO_MODE_PLAYBACK, appUid); } if (streamClass == PA_STREAM) { return std::make_shared(eStreamType, AUDIO_MODE_PLAYBACK, appUid); diff --git a/services/audio_service/test/example/fast_audio_stream_playback_test.cpp b/services/audio_service/test/example/fast_audio_stream_playback_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..642d162bad211a772ebf57be736adf5a7a3a0152 --- /dev/null +++ b/services/audio_service/test/example/fast_audio_stream_playback_test.cpp @@ -0,0 +1,178 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include + +#include "audio_renderer.h" +#include "audio_log.h" +#include "audio_utils.h" +#include "audio_errors.h" +#include "parameter.h" +#include "pcm2wav.h" + +static constexpr int32_t SLEEP_TIME_NS = 2000000000; + +namespace OHOS { +namespace AudioStandard { +class PlaybackTest : public AudioRendererWriteCallback, + public std::enable_shared_from_this { +public: + int32_t InitRenderer(); + int32_t StartPlay(); + void OnWriteData(size_t length) override; + bool OpenSpkFile(const std::string spkFilePath); + +private: + std::unique_ptr audioRenderer_ = nullptr; + static constexpr long WAV_HEADER_SIZE = 44; + int32_t loopCount_ = 2; // play 2 times + bool needSkipWavHeader_ = true; + bool renderFinish_ = false; + FILE *spkWavFile_ = nullptr; +}; + +void PlaybackTest::OnWriteData(size_t length) +{ + BufferDesc bufDesc; + if (audioRenderer_ == nullptr) { + AUDIO_ERR_LOG("audioRenderer is nullptr."); + return; + } + audioRenderer_->GetBufferDesc(bufDesc); + + if (spkWavFile_ == nullptr) { + AUDIO_ERR_LOG("spkWavFile_ is nullptr."); + return; + } + if (needSkipWavHeader_) { + fseek(spkWavFile_, WAV_HEADER_SIZE, SEEK_SET); + needSkipWavHeader_ = false; + } + if (feof(spkWavFile_)) { + loopCount_--; + if (loopCount_ < 0) { + fseek(spkWavFile_, WAV_HEADER_SIZE, SEEK_SET); // infinite loop + } else if (loopCount_ == 0) { + renderFinish_ = true; + } else { + fseek(spkWavFile_, WAV_HEADER_SIZE, SEEK_SET); + } + } + if (renderFinish_) { + AUDIO_INFO_LOG("%{public}s render finish.", __func__); + return; + } + fread(bufDesc.buffer, 1, bufDesc.bufLength, spkWavFile_); + audioRenderer_->Enqueue(bufDesc); +} + +bool PlaybackTest::OpenSpkFile(const std::string spkFilePath) +{ + if (spkWavFile_ != nullptr) { + AUDIO_ERR_LOG("Spk file has been opened, spkFilePath %{public}s", spkFilePath.c_str()); + return true; + } + + char path[PATH_MAX] = { 0x00 }; + if ((strlen(spkFilePath.c_str()) > PATH_MAX) || (realpath(spkFilePath.c_str(), path) == nullptr)) { + return false; + } + AUDIO_INFO_LOG("spk path = %{public}s", path); + spkWavFile_ = fopen(path, "rb"); + if (spkWavFile_ == nullptr) { + AUDIO_ERR_LOG("Unable to open wave file"); + return false; + } + return true; +} + +int32_t PlaybackTest::InitRenderer() +{ + AudioStandard::AudioRendererOptions rendererOptions = { + { + AudioStandard::AudioSamplingRate::SAMPLE_RATE_48000, + AudioStandard::AudioEncodingType::ENCODING_PCM, + AudioStandard::AudioSampleFormat::SAMPLE_S16LE, + AudioStandard::AudioChannel::STEREO, + }, + { + AudioStandard::ContentType::CONTENT_TYPE_MUSIC, + AudioStandard::StreamUsage::STREAM_USAGE_MEDIA, + 4, // fast audio stream + } + }; + audioRenderer_ = AudioStandard::AudioRenderer::Create(rendererOptions); + if (audioRenderer_ == nullptr) { + AUDIO_ERR_LOG("Audio renderer create failed."); + return -1; + } + std::string path = "/data/test.wav"; + OpenSpkFile(path); + int32_t ret = audioRenderer_->SetRendererWriteCallback(shared_from_this()); + CHECK_AND_RETURN_RET_LOG(ret == SUCCESS, ret, "Client test save data callback fail, ret %{public}d.", ret); + AUDIO_INFO_LOG("Audio renderer create success."); + return 0; +} + +int32_t PlaybackTest::StartPlay() +{ + if (audioRenderer_ == nullptr) { + AUDIO_ERR_LOG("Audiorenderer init failed."); + return -1; + } + if (!audioRenderer_->Start()) { + AUDIO_ERR_LOG("Audio renderer start failed."); + return -1; + } + return 0; +} + +bool SetSysPara(std::string key, int32_t &value) +{ + auto res = SetParameter(key.c_str(), std::to_string(value).c_str()); + if (res < 0) { + AUDIO_WARNING_LOG("SetSysPara fail, key:%{public}s res:%{public}d", key.c_str(), res); + return false; + } + AUDIO_INFO_LOG("SetSysPara success."); + return true; +} +} +} + +using namespace OHOS::AudioStandard; +using namespace std; +int main(int argc, char* argv[]) +{ + cout << "oh audio stream test." << endl; + std::shared_ptr playTest = std::make_shared(); + int32_t val = 1; + SetSysPara("persist.multimedia.audio.mmap.enable", val); + + int32_t ret = playTest->InitRenderer(); + if (ret != 0) { + cout << "Init Renderer error !" << endl; + return -1; + } + cout << "Init Renderer success !" << endl; + ret = playTest->StartPlay(); + if (ret != 0) { + cout << "Start Play error !" << endl; + return -1; + } + cout << "Playing ..." << endl; + usleep(SLEEP_TIME_NS); + return 0; +} \ No newline at end of file