diff --git a/frameworks/native/audiostream/include/i_audio_stream.h b/frameworks/native/audiostream/include/i_audio_stream.h index a84c5e80b64094d6a3ac815c998ebf4c6f126d44..39d6b93bc39e3bf223887ec5b7fea5e3cd0efbb2 100644 --- a/frameworks/native/audiostream/include/i_audio_stream.h +++ b/frameworks/native/audiostream/include/i_audio_stream.h @@ -98,6 +98,7 @@ public: virtual ~IAudioStream() = default; + static int32_t GetByteSizePerFrame(const AudioStreamParams ¶ms, size_t &result); static bool IsStreamSupported(int32_t streamFlags, const AudioStreamParams ¶ms); static std::shared_ptr GetPlaybackStream(StreamClass streamClass, AudioStreamParams params, AudioStreamType eStreamType, int32_t appUid); @@ -233,6 +234,20 @@ public: } virtual void SetState() {} + + bool IsFormatValid(uint8_t format); + + bool IsRendererChannelValid(uint8_t channel); + + bool IsCapturerChannelValid(uint8_t channel); + + bool IsEncodingTypeValid(uint8_t encodingType); + + bool IsSamplingRateValid(uint32_t samplingRate); + + bool IsRendererChannelLayoutValid(uint64_t channelLayout); + + bool IsPlaybackChannelRelatedInfoValid(uint8_t channels, uint64_t channelLayout); }; } // namespace AudioStandard } // namespace OHOS diff --git a/frameworks/native/examples/BUILD.gn b/frameworks/native/examples/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..0ed2e1d17a430b1319f36d951b08310e21701564 --- /dev/null +++ b/frameworks/native/examples/BUILD.gn @@ -0,0 +1,50 @@ +# Copyright (c) 2021-2022 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. + +import("//build/ohos.gni") +import("../../../audio_ohcore.gni") +import("../../../config.gni") + +ohos_executable("pa_stream_test") { + install_enable = false + sanitize = { + cfi = true + cfi_cross_dso = true + debug = false + } + cflags = [ + "-Wall", + "-Werror", + ] + + include_dirs = [ + "../../../interfaces/inner_api/native/audiocommon/include", + # "../audiorenderer/include", + # "../audiocapturer/include", + ] + + sources = [ "./pa_stream_test.cpp" ] + + deps = [ + "../audiocapturer:audio_capturer", + "../audiorenderer:audio_renderer", + ] + + external_deps = [ + "c_utils:utils", + "hilog:libhilog", + ] + + part_name = "audio_framework" + subsystem_name = "multimedia" +} diff --git a/frameworks/native/examples/pa_stream_test.cpp b/frameworks/native/examples/pa_stream_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c9f556a8f21d3fdd9a3fa012dec1a22e341d0dbc --- /dev/null +++ b/frameworks/native/examples/pa_stream_test.cpp @@ -0,0 +1,902 @@ +/* + * 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 +#include +#include +#include +#include +#include "securec.h" + +#include "audio_log.h" +#include "audio_renderer.h" +#include "audio_capturer.h" +#include "pcm2wav.h" + +using namespace std; +namespace OHOS { +namespace AudioStandard { +constexpr int32_t SAMPLE_FORMAT_U8 = 8; +constexpr int32_t SAMPLE_FORMAT_S16LE = 16; +constexpr int32_t SAMPLE_FORMAT_S24LE = 24; +constexpr int32_t SAMPLE_FORMAT_S32LE = 32; +constexpr size_t ONE_READ_FRAME = 3840; + +enum RendererMode : int32_t { + DIRECTLY_WRITE = 0, + AFTER_CALLBACK = 1, +}; + +enum CapturerMode : int32_t { + DIRECTLY_READ = 0, + READ_AFTER_CALLBACK = 1, +}; + +enum OperationCode : int32_t { + CODE_INVALID = -1, + RENDERER_CODE_INIT = 0, + RENDERER_CODE_START = 1, + RENDERER_CODE_PAUSE = 2, + RENDERER_CODE_FLUSH = 3, + RENDERER_CODE_DRAIN = 4, + RENDERER_CODE_STOP = 5, + RENDERER_CODE_RELEASE = 6, + RENDERER_CODE_WRITE = 7, + CAPTURER_CODE_INIT = 100, + CAPTURER_CODE_START = 101, + CAPTURER_CODE_PAUSE = 102, + CAPTURER_CODE_FLUSH = 103, + CAPTURER_CODE_STOP = 105, + CAPTURER_CODE_RELEASE = 106, + CAPTURER_CODE_READ = 107, + EXIT_DEMO = 1000, +}; + +std::map g_OptStrMap = { + {RENDERER_CODE_INIT, "call spk init process"}, + {RENDERER_CODE_START, "call start spk process"}, + {RENDERER_CODE_PAUSE, "call pause spk process"}, + {RENDERER_CODE_FLUSH, "call flush spk process"}, + {RENDERER_CODE_DRAIN, "call drain spk process"}, + {RENDERER_CODE_STOP, "call stop spk process"}, + {RENDERER_CODE_RELEASE, "release spk process"}, + {RENDERER_CODE_WRITE, "write data"}, + {CAPTURER_CODE_INIT, "call capturer init process"}, + {CAPTURER_CODE_START, "call start capturer process"}, + {CAPTURER_CODE_PAUSE, "call pause capturer process"}, + {CAPTURER_CODE_FLUSH, "call flush capturer process"}, + {CAPTURER_CODE_STOP, "call stop capturer process"}, + {CAPTURER_CODE_RELEASE, "call release capturer process"}, + {CAPTURER_CODE_READ, "read data"}, + {EXIT_DEMO, "exit interactive run test"}, +}; + +class PaRendererTest : public AudioRendererWriteCallback, public enable_shared_from_this { +public: + virtual ~PaRendererTest() {}; + int32_t InitRenderer(RendererMode rendererMode, int32_t fileIndex); + int32_t StartPlay(); + int32_t PausePlay(); + int32_t FlushPlay(); + int32_t DrainPlay(); + int32_t StopPlay(); + int32_t ReleasePlay(); + int32_t WriteData(); + void WriteDataWorker(); + void OnWriteData(size_t length) override; + AudioSampleFormat GetSampleFormat(int32_t wavSampleFormat) const; + bool OpenSpkFile(const std::string &spkFilePath); + void CloseSpkFile(); + +private: + std::unique_ptr audioRenderer_ = nullptr; + static constexpr long WAV_HEADER_SIZE = 44; + FILE *spkWavFile_ = nullptr; + size_t bytesAlreadyWrite_ = 0; + + std::condition_variable enableWriteCv_; + std::mutex enableWriteThreadLock_; + bool enableWrite_ = false; + int32_t fast_ = 1000; + int32_t slow_ = 30000; + size_t bufferLength_ = 0; + bool isFileOpened_ = false; + wav_hdr wavHeader_; + RendererMode rendererMode_ = DIRECTLY_WRITE; + + std::map filePathMap_ = { + {0, "/data/test.wav"}, + {1, "/data/test2.wav"}, + }; +}; + +AudioSampleFormat PaRendererTest::GetSampleFormat(int32_t wavSampleFormat) const +{ + switch (wavSampleFormat) { + case SAMPLE_FORMAT_U8: + return AudioSampleFormat::SAMPLE_U8; + case SAMPLE_FORMAT_S16LE: + return AudioSampleFormat::SAMPLE_S16LE; + case SAMPLE_FORMAT_S24LE: + return AudioSampleFormat::SAMPLE_S24LE; + case SAMPLE_FORMAT_S32LE: + return AudioSampleFormat::SAMPLE_S32LE; + default: + return AudioSampleFormat::INVALID_WIDTH; + } +} + +bool PaRendererTest::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; +} + +void PaRendererTest::CloseSpkFile() +{ + if (spkWavFile_ != nullptr) { + fclose(spkWavFile_); + spkWavFile_ = nullptr; + } +} + +int32_t PaRendererTest::InitRenderer(RendererMode rendererMode, int32_t fileIndex) +{ + rendererMode_ = rendererMode; + AUDIO_INFO_LOG("Start OpenSpkFile, isFileOpened_: %{public}d", isFileOpened_); + if (isFileOpened_ == false) { + AUDIO_INFO_LOG("Start OpenSpkFile, fileIndex: %{public}d", fileIndex); + std::string path = filePathMap_[fileIndex]; + OpenSpkFile(path); + + size_t headerSize = sizeof(wav_hdr); + size_t bytesRead = fread(&wavHeader_, 1, headerSize, spkWavFile_); + AUDIO_DEBUG_LOG("Init renderer, bytesRead: %{public}zu", bytesRead); + isFileOpened_ = true; + } + ContentType contentType = ContentType::CONTENT_TYPE_MUSIC; + StreamUsage streamUsage = StreamUsage::STREAM_USAGE_MEDIA; + + AudioRendererOptions rendererOptions = {}; + rendererOptions.streamInfo.encoding = AudioEncodingType::ENCODING_PCM; + rendererOptions.streamInfo.samplingRate = static_cast(wavHeader_.SamplesPerSec); + rendererOptions.streamInfo.format = GetSampleFormat(wavHeader_.bitsPerSample); + rendererOptions.streamInfo.channels = static_cast(wavHeader_.NumOfChan); + rendererOptions.streamInfo.channels = static_cast(wavHeader_.NumOfChan); + rendererOptions.rendererInfo.contentType = contentType; + rendererOptions.rendererInfo.streamUsage = streamUsage; + rendererOptions.rendererInfo.rendererFlags = 0; + AUDIO_ERR_LOG("samplingRate %{public}d, format %{public}d, channels %{public}d", + rendererOptions.streamInfo.samplingRate, rendererOptions.streamInfo.format, + rendererOptions.streamInfo.channels); + audioRenderer_ = AudioRenderer::Create(rendererOptions); + if (audioRenderer_ == nullptr) { + AUDIO_ERR_LOG("AudioRendererTest: Create failed"); + return -1; + } + + if (rendererMode_ == AFTER_CALLBACK) { + if (audioRenderer_->SetRenderMode(RENDER_MODE_CALLBACK)) { + AUDIO_ERR_LOG("SetRenderMode failed"); + return false; + } + + if (audioRenderer_->SetRendererWriteCallback(shared_from_this())) { + AUDIO_ERR_LOG("SetRendererWriteCallback failed"); + return false; + } + } + + if (audioRenderer_->GetBufferSize(bufferLength_)) { + return -1; + } + AUDIO_INFO_LOG("Audio renderer create success."); + return 0; +} + +int32_t PaRendererTest::StartPlay() +{ + if (audioRenderer_ == nullptr) { + AUDIO_ERR_LOG("Audiorenderer init failed."); + return -1; + } + if (!audioRenderer_->Start()) { + AUDIO_ERR_LOG("Audio renderer start failed."); + return -1; + } + enableWrite_ = true; + enableWriteCv_.notify_all(); + return 0; +} + +int32_t PaRendererTest::PausePlay() +{ + if (audioRenderer_ == nullptr) { + AUDIO_ERR_LOG("Audiorenderer init failed."); + return -1; + } + enableWrite_ = false; + if (!audioRenderer_->Pause()) { + AUDIO_ERR_LOG("Audio renderer start failed."); + return -1; + } + return 0; +} + +int32_t PaRendererTest::FlushPlay() +{ + if (audioRenderer_ == nullptr) { + AUDIO_ERR_LOG("Audiorenderer init failed."); + return -1; + } + if (!audioRenderer_->Flush()) { + AUDIO_ERR_LOG("Audio renderer start failed."); + return -1; + } + return 0; +} + +int32_t PaRendererTest::DrainPlay() +{ + if (audioRenderer_ == nullptr) { + AUDIO_ERR_LOG("Audiorenderer init failed."); + return -1; + } + if (!audioRenderer_->Drain()) { + AUDIO_ERR_LOG("Audio renderer start failed."); + return -1; + } + return 0; +} + +int32_t PaRendererTest::StopPlay() +{ + if (audioRenderer_ == nullptr) { + AUDIO_ERR_LOG("Audiorenderer init failed."); + return -1; + } + enableWrite_ = false; + if (!audioRenderer_->Stop()) { + AUDIO_ERR_LOG("Audio renderer stop failed."); + return -1; + } + return 0; +} + +int32_t PaRendererTest::ReleasePlay() +{ + if (audioRenderer_ == nullptr) { + AUDIO_ERR_LOG("Audiorenderer init failed."); + return -1; + } + enableWrite_ = false; + if (!audioRenderer_->Release()) { + AUDIO_ERR_LOG("Audio renderer stop failed."); + return -1; + } + audioRenderer_ = nullptr; + return 0; +} + +int32_t PaRendererTest::WriteData() +{ + enableWrite_ = true; + std::thread writeDataThread = std::thread(&PaRendererTest::WriteDataWorker, this); + writeDataThread.detach(); + return 0; +} + +void PaRendererTest::WriteDataWorker() +{ + while (true) { + std::unique_lock threadLock(enableWriteThreadLock_); + enableWriteCv_.wait(threadLock, [this] { + AUDIO_INFO_LOG("enable write state: %{public}d", enableWrite_); + return enableWrite_; + }); + + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_int_distribution<> dis(fast_, slow_); + int32_t randomNum = dis(gen); + AUDIO_INFO_LOG("recorder sleepTime %{public}d", randomNum); + usleep(randomNum); + if (audioRenderer_ == nullptr) { + AUDIO_ERR_LOG("Audiorenderer init failed."); + enableWrite_ = false; + return ; + } + if (spkWavFile_ == nullptr) { + AUDIO_ERR_LOG("wavFile is nullptr"); + enableWrite_ = false; + return ; + } + if (feof(spkWavFile_)) { + fseek(spkWavFile_, WAV_HEADER_SIZE, SEEK_SET); + } + + if (rendererMode_ == DIRECTLY_WRITE) { + auto buffer = std::make_unique(bufferLength_); + AUDIO_ERR_LOG("WriteDataWorker: bufferLength_ %{public}zu", bufferLength_); + fread(buffer.get(), 1, bufferLength_, spkWavFile_); + bytesAlreadyWrite_ += audioRenderer_->Write(buffer.get(), bufferLength_); + AUDIO_INFO_LOG("bytesAlreadyWrite_: %{public}zu, bufferLength_: %{public}zu", + bytesAlreadyWrite_, bufferLength_); + } + } +} + +void PaRendererTest::OnWriteData(size_t length) +{ + AUDIO_INFO_LOG("On write data callback, length %{public}zu", length); + BufferDesc currentWriteBuffer = { nullptr, 0, 0}; + audioRenderer_->GetBufferDesc(currentWriteBuffer); + if (currentWriteBuffer.buffer == nullptr) { + return ; + } + if (length > currentWriteBuffer.bufLength) { + currentWriteBuffer.dataLength = currentWriteBuffer.bufLength; + } else { + currentWriteBuffer.dataLength = length; + } + fread(currentWriteBuffer.buffer, 1, currentWriteBuffer.dataLength, spkWavFile_); + bytesAlreadyWrite_ += currentWriteBuffer.dataLength; + audioRenderer_->Enqueue(currentWriteBuffer); + AUDIO_INFO_LOG("Callback mode, bytesAlreadyWrite_: %{public}zu, length: %{public}zu", + bytesAlreadyWrite_, length); +} + +class PaCapturerTest : public AudioCapturerReadCallback, public enable_shared_from_this { +public: + virtual ~PaCapturerTest() {}; + + int32_t InitCapturer(bool isBlocking, CapturerMode capturerMode); + int32_t StartRecorder(); + int32_t PauseRecorder(); + int32_t FlushRecorder(); + int32_t StopRecorder(); + int32_t ReleaseRecorder(); + int32_t ReadData(); + void ReadDataWorker(); + void OnReadData(size_t length) override; + +private: + std::unique_ptr audioCapturer_ = nullptr; + bool isBlocking_ = false; + + std::condition_variable enableReadCv_; + std::mutex enableReadThreadLock_; + bool enableRead_ = false; + int32_t fast_ = 1; // min sleep time + int32_t slow_ = 2; // max sleep time + FILE *pfd_ = nullptr; + CapturerMode capturerMode_; +}; + +void PaCapturerTest::OnReadData(size_t length) +{ + AUDIO_INFO_LOG("PaCapturerTest::OnReadData, length: %{public}zu", length); + BufferDesc bufferDesc = { nullptr, 0, 0 }; + audioCapturer_->GetBufferDesc(bufferDesc); + fwrite(reinterpret_cast(bufferDesc.buffer), 1, bufferDesc.bufLength, pfd_); + audioCapturer_->Enqueue(bufferDesc); +} + +int32_t PaCapturerTest::InitCapturer(bool isBlocking, CapturerMode capturerMode) +{ + AUDIO_INFO_LOG("Start InitCapturer"); + isBlocking_ = isBlocking; + capturerMode_ = capturerMode; + AudioCapturerOptions capturerOptions; + capturerOptions.streamInfo.samplingRate = SAMPLE_RATE_44100; + capturerOptions.streamInfo.encoding = AudioEncodingType::ENCODING_PCM; + capturerOptions.streamInfo.format = AudioSampleFormat::SAMPLE_S16LE; + capturerOptions.streamInfo.channels = AudioChannel::STEREO; + capturerOptions.capturerInfo.sourceType = SourceType::SOURCE_TYPE_MIC; + capturerOptions.capturerInfo.capturerFlags = 0; + + audioCapturer_ = AudioCapturer::Create(capturerOptions); + if (audioCapturer_ == nullptr) { + AUDIO_ERR_LOG("Create audioCapturer failed"); + return -1; + } + if (capturerMode_ == READ_AFTER_CALLBACK) { + if (audioCapturer_->SetCaptureMode(CAPTURE_MODE_CALLBACK)) { + AUDIO_ERR_LOG("SetCaptureMode failed"); + return -1; + } + if (audioCapturer_->SetCapturerReadCallback(shared_from_this())) { + AUDIO_ERR_LOG("SetCapturerReadCallback failed"); + return -1; + } + } + AUDIO_INFO_LOG("Audio capturer create success."); + pfd_ = fopen("/data/data/.pulse_dir/capturer.pcm", "wb+"); + return 0; +} + +int32_t PaCapturerTest::StartRecorder() +{ + AUDIO_INFO_LOG("StartRecorder"); + if (audioCapturer_ == nullptr) { + AUDIO_ERR_LOG("audioCapturer_ init failed."); + return -1; + } + enableRead_ = true; + enableReadCv_.notify_all(); + if (!audioCapturer_->Start()) { + AUDIO_ERR_LOG("Audio capturer start failed."); + return -1; + } + return 0; +} + +int32_t PaCapturerTest::PauseRecorder() +{ + if (audioCapturer_ == nullptr) { + AUDIO_ERR_LOG("audioCapturer_ init failed."); + return -1; + } + enableRead_ = false; + if (!audioCapturer_->Pause()) { + AUDIO_ERR_LOG("Audio capturer start failed."); + return -1; + } + return 0; +} + +int32_t PaCapturerTest::FlushRecorder() +{ + if (audioCapturer_ == nullptr) { + AUDIO_ERR_LOG("audioCapturer_ init failed."); + return -1; + } + if (!audioCapturer_->Flush()) { + AUDIO_ERR_LOG("Audio capturer start failed."); + return -1; + } + return 0; +} + +int32_t PaCapturerTest::StopRecorder() +{ + if (audioCapturer_ == nullptr) { + AUDIO_ERR_LOG("audioCapturer_ init failed."); + return -1; + } + if (!audioCapturer_->Stop()) { + AUDIO_ERR_LOG("Audio capturer stop failed."); + return -1; + } + return 0; +} + +int32_t PaCapturerTest::ReleaseRecorder() +{ + if (audioCapturer_ == nullptr) { + AUDIO_ERR_LOG("audioCapturer_ init failed."); + return -1; + } + enableRead_ = false; + if (!audioCapturer_->Release()) { + AUDIO_ERR_LOG("Audio capturer stop failed."); + return -1; + } + audioCapturer_ = nullptr; + fclose(pfd_); + return 0; +} + +int32_t PaCapturerTest::ReadData() +{ + std::thread readDataThread = std::thread(&PaCapturerTest::ReadDataWorker, this); + readDataThread.detach(); + return 0; +} + +void PaCapturerTest::ReadDataWorker() +{ + while (true) { + std::unique_lock threadLock(enableReadThreadLock_); + enableReadCv_.wait(threadLock, [this] { + AUDIO_INFO_LOG("enable read state: %{public}d", enableRead_); + return enableRead_; + }); + AUDIO_INFO_LOG("ReadDataWorker"); + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_int_distribution<> dis(fast_, slow_); + + uint8_t *buffer = (uint8_t *) malloc(ONE_READ_FRAME); + memset_s(buffer, ONE_READ_FRAME, 0, ONE_READ_FRAME); + int32_t currentReadIndex = 0; + while (currentReadIndex < ONE_READ_FRAME) { + int32_t len = audioCapturer_->Read(*(buffer + currentReadIndex), + ONE_READ_FRAME - currentReadIndex, isBlocking_); + currentReadIndex += len; + } + fwrite(reinterpret_cast(buffer), 1, ONE_READ_FRAME, pfd_); + } +} + +int32_t GetUserInput() +{ + int32_t res = -1; // result + size_t count = 3; // try three time + cout << ">>"; + cin >> res; + while (cin.fail() && count-- > 0) { + cin.clear(); + cin.ignore(); + cout << "invalid input, not a number! Please retry with a number." << endl; + cout << ">>"; + cin >> res; + } + return res; +} + +void PrintUsage() +{ + cout << "[Pa Stream Test App]" << endl << endl; + cout << "Supported Functionalities:" << endl; + cout << "================================Usage=======================================" << endl << endl; + cout << " 0: Init renderer." << endl; + cout << " 1: Start play." << endl; + cout << " 2: Pause play." << endl; + cout << " 3: Flush play." << endl; + cout << " 4: Drain play." << endl; + cout << " 5: Stop play." << endl; + cout << " 6: Release play." << endl; + cout << " 7: Write data." << endl; + + cout << " 100: Init renderer." << endl; + cout << " 101: Start play." << endl; + cout << " 102: Pause play." << endl; + cout << " 103: Flush play." << endl; + cout << " 105: Stop play." << endl; + cout << " 106: Release play." << endl; + cout << " 107: Read data." << endl; + + cout << " 1000: exit demo." << endl; + cout << " Please input your choice: " << endl; +} + +int32_t InitPlayback(std::shared_ptr streamTest, RendererMode rendererMode, int32_t fileIndex) +{ + if (streamTest == nullptr) { + cout << "PaRendererTest obj is nullptr, init spk error." << endl; + return -1; + } + int32_t ret = streamTest->InitRenderer(rendererMode, fileIndex); + if (ret != 0) { + cout << "Init renderer error!" << endl; + return -1; + } + return 0; +} + +int32_t StartPlay(std::shared_ptr streamTest) +{ + if (streamTest == nullptr) { + cout << "PaRendererTest obj is nullptr, start play error." << endl; + return -1; + } + int32_t ret = streamTest->StartPlay(); + if (ret != 0) { + cout << "Start play error!" << endl; + return -1; + } + return 0; +} + +int32_t PausePlay(std::shared_ptr streamTest) +{ + if (streamTest == nullptr) { + cout << "PaRendererTest obj is nullptr, pause play error." << endl; + return -1; + } + int32_t ret = streamTest->PausePlay(); + if (ret != 0) { + cout << "Pause play error!" << endl; + return -1; + } + return 0; +} + +int32_t FlushPlay(std::shared_ptr streamTest) +{ + if (streamTest == nullptr) { + cout << "PaRendererTest obj is nullptr, Flush play error." << endl; + return -1; + } + int32_t ret = streamTest->FlushPlay(); + if (ret != 0) { + cout << "Flush play error!" << endl; + return -1; + } + return 0; +} + +int32_t DrainPlay(std::shared_ptr streamTest) +{ + if (streamTest == nullptr) { + cout << "PaRendererTest obj is nullptr, Drain play error." << endl; + return -1; + } + int32_t ret = streamTest->DrainPlay(); + if (ret != 0) { + cout << "Drain play error!" << endl; + return -1; + } + return 0; +} + +int32_t StopPlay(std::shared_ptr streamTest) +{ + if (streamTest == nullptr) { + cout << "PaRendererTest obj is nullptr, stop play error." << endl; + return -1; + } + int32_t ret = streamTest->StopPlay(); + if (ret != 0) { + cout << "Stop play error!" << endl; + return -1; + } + return 0; +} + +int32_t ReleasePlay(std::shared_ptr streamTest) +{ + if (streamTest == nullptr) { + cout << "PaRendererTest obj is nullptr, stop play error." << endl; + return -1; + } + int32_t ret = streamTest->ReleasePlay(); + if (ret != 0) { + cout << "Stop play error!" << endl; + return -1; + } + return 0; +} + +int32_t WriteData(std::shared_ptr streamTest) +{ + if (streamTest == nullptr) { + cout << "PaRendererTest obj is nullptr, stop play error." << endl; + return -1; + } + int32_t ret = streamTest->WriteData(); + if (ret != 0) { + cout << "Stop play error!" << endl; + return -1; + } + return 0; +} + +int32_t InitRecorder(std::shared_ptr capturerTest, bool isBlocking, CapturerMode capturerMode) +{ + if (capturerTest == nullptr) { + cout << "PaRendererTest obj is nullptr, init recorder error." << endl; + return -1; + } + int32_t ret = capturerTest->InitCapturer(isBlocking, capturerMode); + if (ret != 0) { + cout << "Init capturer error!" << endl; + return -1; + } + return 0; +} + +int32_t StartRecorder(std::shared_ptr capturerTest) +{ + if (capturerTest == nullptr) { + cout << "PaRendererTest obj is nullptr, start recorder error." << endl; + return -1; + } + int32_t ret = capturerTest->StartRecorder(); + if (ret != 0) { + cout << "Start recorder error!" << endl; + return -1; + } + return 0; +} + +int32_t PauseRecorder(std::shared_ptr capturerTest) +{ + if (capturerTest == nullptr) { + cout << "PaRendererTest obj is nullptr, pause recorder error." << endl; + return -1; + } + int32_t ret = capturerTest->PauseRecorder(); + if (ret != 0) { + cout << "Pause recorder error!" << endl; + return -1; + } + return 0; +} + +int32_t FlushRecorder(std::shared_ptr capturerTest) +{ + if (capturerTest == nullptr) { + cout << "PaRendererTest obj is nullptr, Flush recorder error." << endl; + return -1; + } + int32_t ret = capturerTest->FlushRecorder(); + if (ret != 0) { + cout << "Flush recorder error!" << endl; + return -1; + } + return 0; +} + +int32_t StopRecorder(std::shared_ptr capturerTest) +{ + if (capturerTest == nullptr) { + cout << "PaRendererTest obj is nullptr, stop recorder error." << endl; + return -1; + } + int32_t ret = capturerTest->StopRecorder(); + if (ret != 0) { + cout << "Stop recorder error!" << endl; + return -1; + } + return 0; +} + +int32_t ReleaseRecorder(std::shared_ptr capturerTest) +{ + if (capturerTest == nullptr) { + cout << "PaRendererTest obj is nullptr, stop recorder error." << endl; + return -1; + } + int32_t ret = capturerTest->ReleaseRecorder(); + if (ret != 0) { + cout << "Stop recorder error!" << endl; + return -1; + } + return 0; +} + +int32_t ReadData(std::shared_ptr capturerTest) +{ + if (capturerTest == nullptr) { + cout << "PaCapturerTest obj is nullptr, read data error." << endl; + return -1; + } + int32_t ret = capturerTest->ReadData(); + if (ret != 0) { + cout << "Read data error!" << endl; + return -1; + } + return 0; +} + +void HandleCapturerCode(OperationCode optCode, std::shared_ptr streamTest, + std::shared_ptr capturerTest) +{ + switch (optCode) { + case RENDERER_CODE_START: + StartPlay(streamTest); + break; + case RENDERER_CODE_PAUSE: + PausePlay(streamTest); + break; + case RENDERER_CODE_FLUSH: + FlushPlay(streamTest); + break; + case RENDERER_CODE_DRAIN: + DrainPlay(streamTest); + break; + case RENDERER_CODE_STOP: + StopPlay(streamTest); + break; + case RENDERER_CODE_RELEASE: + ReleasePlay(streamTest); + break; + case RENDERER_CODE_WRITE: + WriteData(streamTest); + break; + case CAPTURER_CODE_START: + StartRecorder(capturerTest); + break; + case CAPTURER_CODE_PAUSE: + PauseRecorder(capturerTest); + break; + case CAPTURER_CODE_FLUSH: + FlushRecorder(capturerTest); + break; + case CAPTURER_CODE_STOP: + StopRecorder(capturerTest); + break; + case CAPTURER_CODE_RELEASE: + ReleaseRecorder(capturerTest); + break; + case CAPTURER_CODE_READ: + ReadData(capturerTest); + break; + default: + cout << "Invalid input: " << optCode << endl; + break; + } +} + +void Loop(std::shared_ptr streamTest, std::shared_ptr capturerTest) +{ + bool isProcTestRun = true; + while (isProcTestRun) { + PrintUsage(); + OperationCode optCode = CODE_INVALID; + int32_t res = GetUserInput(); + int32_t fileIndex = -1; + int32_t rendererMode = 0; + int32_t isBlocking = 0; + int32_t capturerMode = 0; + + if (g_OptStrMap.count(res)) { + optCode = static_cast(res); + } + switch (optCode) { + case RENDERER_CODE_INIT: + rendererMode = GetUserInput(); + fileIndex = GetUserInput(); + InitPlayback(streamTest, static_cast(rendererMode), fileIndex); + break; + // Capturer + case CAPTURER_CODE_INIT: + isBlocking = GetUserInput(); + capturerMode = GetUserInput(); + InitRecorder(capturerTest, isBlocking, static_cast(capturerMode)); + break; + case EXIT_DEMO: + isProcTestRun = false; + break; + default: + HandleCapturerCode(optCode, streamTest, capturerTest); + break; + } + } +} +} +} + +using namespace OHOS::AudioStandard; +using namespace std; +int main(int argc, char* argv[]) +{ + cout << "oh pa stream test." << endl; + std::shared_ptr streamTest = std::make_shared(); + std::shared_ptr capturerTest = std::make_shared(); + + Loop(streamTest, capturerTest); + streamTest->CloseSpkFile(); + return 0; +} diff --git a/frameworks/native/examples/pcm2wav.h b/frameworks/native/examples/pcm2wav.h new file mode 100644 index 0000000000000000000000000000000000000000..97205999d8a1fb1ab6758e9c0cdcd728d039f686 --- /dev/null +++ b/frameworks/native/examples/pcm2wav.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2021-2022 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 PCM_2_WAV_H +#define PCM_2_WAV_H + +#include + +struct WAV_HEADER { + /* RIFF Chunk Descriptor */ + uint8_t RIFF[4] = {'R', 'I', 'F', 'F'}; // RIFF Header Magic header + uint32_t ChunkSize = 0; // RIFF Chunk Size + uint8_t WAVE[4] = {'W', 'A', 'V', 'E'}; // WAVE Header + /* "fmt" sub-chunk */ + uint8_t fmt[4] = {'f', 'm', 't', ' '}; // FMT header + uint32_t Subchunk1Size = 16; // Size of the fmt chunk + uint16_t AudioFormat = 1; // Audio format 1=PCM + uint16_t NumOfChan = 2; // Number of channels 1=Mono 2=Stereo + uint32_t SamplesPerSec = 44100; // Sampling Frequency in Hz + uint32_t bytesPerSec = 176400; // bytes per second + uint16_t blockAlign = 2; // 2=16-bit mono, 4=16-bit stereo + uint16_t bitsPerSample = 16; // Number of bits per sample + /* "data" sub-chunk */ + uint8_t Subchunk2ID[4] = {'d', 'a', 't', 'a'}; // "data" string + uint32_t Subchunk2Size = 0; // Sampled data length +}; + +using wav_hdr = struct WAV_HEADER; +#endif // PCM_2_WAV_H diff --git a/frameworks/native/opensles/src/adapter/audiocapturer_adapter.cpp b/frameworks/native/opensles/src/adapter/audiocapturer_adapter.cpp index 56c806c0f89414c8377115d31ecafe30ab6ec187..765fe7a63966a4307ca1080a55a3462af851f147 100644 --- a/frameworks/native/opensles/src/adapter/audiocapturer_adapter.cpp +++ b/frameworks/native/opensles/src/adapter/audiocapturer_adapter.cpp @@ -147,6 +147,7 @@ SLresult AudioCapturerAdapter::EnqueueAdapter(SLuint32 id, const void *buffer, S BufferDesc bufDesc = {}; bufDesc.buffer = (uint8_t*) buffer; bufDesc.bufLength = size; + bufDesc.dataLength = size; AUDIO_INFO_LOG("AudioCapturerAdapter::EnqueueAdapter bufferlength: %{public}zu", bufDesc.bufLength); audioCapturer->Enqueue(bufDesc); return SL_RESULT_SUCCESS; diff --git a/frameworks/native/opensles/src/adapter/audioplayer_adapter.cpp b/frameworks/native/opensles/src/adapter/audioplayer_adapter.cpp index b90ddccfdb3725f43f15bbd93a910b4b4a93fd60..b5b246506f2636dc4df7464fe105e4186aa6f7bb 100644 --- a/frameworks/native/opensles/src/adapter/audioplayer_adapter.cpp +++ b/frameworks/native/opensles/src/adapter/audioplayer_adapter.cpp @@ -185,6 +185,7 @@ SLresult AudioPlayerAdapter::EnqueueAdapter(SLuint32 id, const void *buffer, SLu BufferDesc bufDesc = {}; bufDesc.buffer = (uint8_t*) buffer; bufDesc.bufLength = size; + bufDesc.dataLength = size; audioRenderer->Enqueue(bufDesc); return SL_RESULT_SUCCESS; } diff --git a/frameworks/native/pulseaudio/modules/hdi/hdi_sink.c b/frameworks/native/pulseaudio/modules/hdi/hdi_sink.c index cf60b6039a3f8f483f246c677de0e05724cc14a5..3416ce4bac40cb4650a8fcb8d7669ccade66bfff 100644 --- a/frameworks/native/pulseaudio/modules/hdi/hdi_sink.c +++ b/frameworks/native/pulseaudio/modules/hdi/hdi_sink.c @@ -2492,7 +2492,7 @@ static void SetHdiParam(struct Userdata *userdata) bool spatializationEnabled = pa_safe_streq(sinkSpatialization, "1") ? true : false; bool effectEnabled = pa_safe_streq(sinkSceneMode, "EFFECT_DEFAULT") ? true : false; bool spatialEnabled = spatializationEnabled && effectEnabled; - int sessionID = atoi(sinkSessionStr); + int sessionID = atoi(sinkSessionStr == NULL ? "-1" : sinkSessionStr); if (sessionID > sessionIDMax && !sinkSceneType && !sinkSceneMode && !sinkSpatialization) { sessionIDMax = sessionID; sinkSceneTypeMax = (char *)sinkSceneType; diff --git a/interfaces/inner_api/native/audiocommon/include/audio_info.h b/interfaces/inner_api/native/audiocommon/include/audio_info.h index e40437ce26d1cdf5191661a666d43b9c0c4d0602..ad56afa1c6d4d144c39f9157b03dfe840be13a50 100644 --- a/interfaces/inner_api/native/audiocommon/include/audio_info.h +++ b/interfaces/inner_api/native/audiocommon/include/audio_info.h @@ -624,6 +624,12 @@ struct AudioProcessConfig { AudioCapturerInfo capturerInfo; AudioStreamType streamType; + + bool isInnerCapturer; + + bool isWakeupCapturer; + + // Waiting for review: add isWakeupCapturer isInnerCapturer }; struct Volume { diff --git a/interfaces/inner_api/native/audiocommon/include/audio_stream_info.h b/interfaces/inner_api/native/audiocommon/include/audio_stream_info.h index a7d4a1542cc995fd815919881530d1aa5b4e0375..8020213fae2c7933d35c19eb2204a65bb15a036b 100644 --- a/interfaces/inner_api/native/audiocommon/include/audio_stream_info.h +++ b/interfaces/inner_api/native/audiocommon/include/audio_stream_info.h @@ -223,7 +223,7 @@ enum AudioEncodingType { // format -enum AudioSampleFormat { +enum AudioSampleFormat : uint8_t { SAMPLE_U8 = 0, SAMPLE_S16LE = 1, SAMPLE_S24LE = 2, @@ -233,7 +233,7 @@ enum AudioSampleFormat { }; // channel -enum AudioChannel { +enum AudioChannel : uint8_t { MONO = 1, STEREO = 2, CHANNEL_3 = 3, diff --git a/services/audio_service/BUILD.gn b/services/audio_service/BUILD.gn index 5b3bc8d4c96c70527182c372507daecacf7986ba..86eb1384ec074d30d5660f70da81a4fcff86a015 100644 --- a/services/audio_service/BUILD.gn +++ b/services/audio_service/BUILD.gn @@ -44,8 +44,10 @@ ohos_shared_library("audio_common") { sources = [ "common/src/audio_process_config.cpp", + "common/src/audio_ring_cache.cpp", "common/src/linear_pos_time_model.cpp", "common/src/oh_audio_buffer.cpp", + "common/src/volume_tools.cpp", ] cflags = [ @@ -145,10 +147,16 @@ 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/callback_handler.cpp", + "client/src/capturer_in_client.cpp", "client/src/fast_audio_stream.cpp", "client/src/i_audio_stream.cpp", + "client/src/ipc_stream_listener_impl.cpp", + "client/src/ipc_stream_listener_stub.cpp", + "client/src/ipc_stream_proxy.cpp", "client/src/microphone_descriptor.cpp", "client/src/policy_provider_stub.cpp", + "client/src/renderer_in_client.cpp", ] public_configs = [ ":audio_client_public_config" ] @@ -230,6 +238,10 @@ config("audio_service_config") { "common/include", "client/include", "server/include", + "$pulseaudio_dir/src", + "$pulseaudio_dir/confgure/src", + "$pulseaudio_dir/include", + "$pulseaudio_dir/include/pulse", "../audio_policy/server/include/service/effect", "../../frameworks/native/audioeffect/include", "../../frameworks/native/audiopolicy/include", @@ -276,14 +288,24 @@ ohos_shared_library("audio_process_service") { "server/src/audio_process_in_server.cpp", "server/src/audio_process_stub.cpp", "server/src/audio_service.cpp", + "server/src/capturer_in_server.cpp", + "server/src/i_stream_manager.cpp", + "server/src/ipc_stream_in_server.cpp", + "server/src/ipc_stream_listener_proxy.cpp", + "server/src/ipc_stream_stub.cpp", + "server/src/pa_adapter_manager.cpp", + "server/src/pa_capturer_stream_impl.cpp", + "server/src/pa_renderer_stream_impl.cpp", "server/src/policy_handler.cpp", "server/src/policy_provider_proxy.cpp", + "server/src/renderer_in_server.cpp", ] configs = [ ":audio_service_config" ] deps = [ ":audio_common", + "$pulseaudio_build_path/src/pulse:pulse", "../../frameworks/native/audioschedule:audio_schedule", "../../frameworks/native/audioutils:audio_utils", "../../frameworks/native/hdiadapter/sink:fast_audio_renderer_sink", diff --git a/services/audio_service/client/include/callback_handler.h b/services/audio_service/client/include/callback_handler.h new file mode 100755 index 0000000000000000000000000000000000000000..2131ba30f7a5ecb39f6581bb5e302e237a3e90f5 --- /dev/null +++ b/services/audio_service/client/include/callback_handler.h @@ -0,0 +1,42 @@ +/* + * 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 CALLBACK_HANDLER_H +#define CALLBACK_HANDLER_H + +#include +#include + +namespace OHOS { +namespace AudioStandard { + +class IHandler { +public: + virtual ~IHandler() = default; + virtual void OnHandle(uint32_t code, int64_t data) = 0; +}; + +class CallbackHandler { +public: + virtual ~CallbackHandler() = default; + static std::shared_ptr GetInstance(std::shared_ptr iHandler); + + virtual void SendCallbackEvent(uint32_t code, int64_t data) = 0; + + virtual void ReleaseEventRunner() = 0; +}; +} // namespace AudioStandard +} // namespace OHOS +#endif // CALLBACK_HANDLER_H diff --git a/services/audio_service/client/include/capturer_in_client.h b/services/audio_service/client/include/capturer_in_client.h new file mode 100644 index 0000000000000000000000000000000000000000..4ce0a0893add1a103c3f6caa2a5ea3b5f331a890 --- /dev/null +++ b/services/audio_service/client/include/capturer_in_client.h @@ -0,0 +1,29 @@ +/* + * 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 CAPTURER_IN_SERVER_H +#define CAPTURER_IN_SERVER_H + +#include "i_audio_stream.h" + +namespace OHOS { +namespace AudioStandard { +class CapturerInClient : public IAudioStream { +public: + virtual ~CapturerInClient() = default; + static std::shared_ptr GetInstance(AudioStreamType eStreamType, int32_t appUid); +}; +} // namespace AudioStandard +} // namespace OHOS +#endif // RENDERER_IN_SERVER_H diff --git a/services/audio_service/client/include/ipc_stream_listener_impl.h b/services/audio_service/client/include/ipc_stream_listener_impl.h new file mode 100644 index 0000000000000000000000000000000000000000..2ea2bd496a6d81abe5c73627e6b57b7fd5973e97 --- /dev/null +++ b/services/audio_service/client/include/ipc_stream_listener_impl.h @@ -0,0 +1,38 @@ +/* + * 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 IPC_STREAM_LISTERNER_IMPL_H +#define IPC_STREAM_LISTERNER_IMPL_H + +#include "message_parcel.h" + +#include "ipc_stream_listener_stub.h" + +namespace OHOS { +namespace AudioStandard { +// IpcStreamListenerImpl --> sptr | Renderer/CapturerInClientInner --> shared_ptr +class IpcStreamListenerImpl : public IpcStreamListenerStub { +public: + IpcStreamListenerImpl(std::shared_ptr innerListener); + virtual ~IpcStreamListenerImpl() = default; + + // IpcStreamListenerStub + int32_t OnOperationHandled(Operation operation, int64_t result) override; +private: + std::weak_ptr innerListener_; +}; +} // namespace AudioStandard +} // namespace OHOS +#endif // IPC_STREAM_LISTERNER_IMPL_H diff --git a/services/audio_service/client/include/ipc_stream_listener_stub.h b/services/audio_service/client/include/ipc_stream_listener_stub.h new file mode 100644 index 0000000000000000000000000000000000000000..76b86082c599d3d265362d65bdbf8624e095f76e --- /dev/null +++ b/services/audio_service/client/include/ipc_stream_listener_stub.h @@ -0,0 +1,41 @@ +/* + * 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 IPC_STREAM_LISTERNER_STUB_H +#define IPC_STREAM_LISTERNER_STUB_H + +#include "message_parcel.h" + +#include "ipc_stream.h" + +namespace OHOS { +namespace AudioStandard { +class IpcStreamListenerStub : public IRemoteStub { +public: + virtual ~IpcStreamListenerStub() = default; + int OnRemoteRequest(uint32_t code, MessageParcel &data, MessageParcel &reply, MessageOption &option) override; +private: + static bool CheckInterfaceToken(MessageParcel &data); + + int32_t HandleOnOperationHandled(MessageParcel &data, MessageParcel &reply); + + using HandlerFunc = int32_t(IpcStreamListenerStub::*)(MessageParcel &data, MessageParcel &reply); + static inline HandlerFunc funcList_[IpcStreamListenerMsg::IPC_STREAM_LISTENER_MAX_MSG] = { + &IpcStreamListenerStub::HandleOnOperationHandled + }; +}; +} // namespace AudioStandard +} // namespace OHOS +#endif // IPC_STREAM_LISTERNER_STUB_H diff --git a/services/audio_service/client/include/ipc_stream_proxy.h b/services/audio_service/client/include/ipc_stream_proxy.h new file mode 100644 index 0000000000000000000000000000000000000000..3368a6e0cb94765a11cdbc544d9b21f11f24399a --- /dev/null +++ b/services/audio_service/client/include/ipc_stream_proxy.h @@ -0,0 +1,74 @@ +/* + * 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 IPC_STREAM_PROXY_H +#define IPC_STREAM_PROXY_H + +#include "message_parcel.h" + +#include "ipc_stream.h" + +namespace OHOS { +namespace AudioStandard { +class IpcStreamProxy : public IRemoteProxy { +public: + explicit IpcStreamProxy(const sptr &impl); + virtual ~IpcStreamProxy(); + + int32_t RegisterStreamListener(sptr object) override; + + int32_t ResolveBuffer(std::shared_ptr &buffer) override; + + int32_t UpdatePosition() override; + + int32_t GetAudioSessionID(uint32_t &sessionId) override; + + int32_t Start() override; + + int32_t Pause() override; + + int32_t Stop() override; + + int32_t Release() override; + + int32_t Flush() override; + + int32_t Drain() override; + + int32_t GetAudioTime(uint64_t &framePos, uint64_t &timeStamp) override; + + int32_t GetLatency(uint64_t &latency) override; + + int32_t SetRate(int32_t rate) override; // SetRenderRate + + int32_t GetRate(int32_t &rate) override; // SetRenderRate + + int32_t SetLowPowerVolume(float volume) override; // renderer only + + int32_t GetLowPowerVolume(float &volume) override; // renderer only + + int32_t SetAudioEffectMode(int32_t effectMode) override; // renderer only + + int32_t GetAudioEffectMode(int32_t &effectMode) override; // renderer only + + int32_t SetPrivacyType(int32_t privacyType) override; // renderer only + + int32_t GetPrivacyType(int32_t &privacyType) override; // renderer only +private: + static inline BrokerDelegator delegator_; +}; +} // namespace AudioStandard +} // namespace OHOS +#endif // IPC_STREAM_PROXY_H diff --git a/services/audio_service/client/include/renderer_in_client.h b/services/audio_service/client/include/renderer_in_client.h new file mode 100644 index 0000000000000000000000000000000000000000..48069b9f689c9d94474c5a5f38450d4316688f13 --- /dev/null +++ b/services/audio_service/client/include/renderer_in_client.h @@ -0,0 +1,29 @@ +/* + * 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 RENDERER_IN_SERVER_H +#define RENDERER_IN_SERVER_H + +#include "i_audio_stream.h" + +namespace OHOS { +namespace AudioStandard { +class RendererInClient : public IAudioStream { +public: + virtual ~RendererInClient() = default; + static std::shared_ptr GetInstance(AudioStreamType eStreamType, int32_t appUid); +}; +} // namespace AudioStandard +} // namespace OHOS +#endif // RENDERER_IN_SERVER_H 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 6e2aac8c8b891e48ea20b0a6e291ac60f76730ad..6d108f7c99a1e1b4cc533fd67583b747baa50f08 100644 --- a/services/audio_service/client/src/audio_process_in_client.cpp +++ b/services/audio_service/client/src/audio_process_in_client.cpp @@ -969,9 +969,10 @@ int32_t AudioProcessInClientInner::Release() { Trace traceWithLog("AudioProcessInClient::Release", true); CHECK_AND_RETURN_RET_LOG(isInited_, ERR_ILLEGAL_STATE, "not inited!"); - + AUDIO_INFO_LOG("AudioProcessInClientInner::Release()"); // not lock as status is already released if (streamStatus_->load() == StreamStatus::STREAM_RELEASED) { + AUDIO_INFO_LOG("Stream status is already released"); return SUCCESS; } Stop(); diff --git a/services/audio_service/client/src/audio_stream.cpp b/services/audio_service/client/src/audio_stream.cpp index bfa0cc46617ef3c7c77b344beec4c61d7353ad26..1cfb5ffee94cf442e0632b7f7f4ee8de47e454a6 100644 --- a/services/audio_service/client/src/audio_stream.cpp +++ b/services/audio_service/client/src/audio_stream.cpp @@ -263,70 +263,6 @@ vector AudioStream::GetSupportedSamplingRates() const return AUDIO_SUPPORTED_SAMPLING_RATES; } -bool IsFormatValid(uint8_t format) -{ - bool isValidFormat = (find(AUDIO_SUPPORTED_FORMATS.begin(), AUDIO_SUPPORTED_FORMATS.end(), format) - != AUDIO_SUPPORTED_FORMATS.end()); - AUDIO_DEBUG_LOG("AudioStream: IsFormatValid: %{public}s", isValidFormat ? "true" : "false"); - return isValidFormat; -} - -bool IsRendererChannelValid(uint8_t channel) -{ - bool isValidChannel = (find(RENDERER_SUPPORTED_CHANNELS.begin(), RENDERER_SUPPORTED_CHANNELS.end(), channel) - != RENDERER_SUPPORTED_CHANNELS.end()); - AUDIO_DEBUG_LOG("AudioStream: IsChannelValid: %{public}s", isValidChannel ? "true" : "false"); - return isValidChannel; -} - -bool IsCapturerChannelValid(uint8_t channel) -{ - bool isValidChannel = (find(CAPTURER_SUPPORTED_CHANNELS.begin(), CAPTURER_SUPPORTED_CHANNELS.end(), channel) - != CAPTURER_SUPPORTED_CHANNELS.end()); - AUDIO_DEBUG_LOG("AudioStream: IsChannelValid: %{public}s", isValidChannel ? "true" : "false"); - return isValidChannel; -} - -bool IsEncodingTypeValid(uint8_t encodingType) -{ - bool isValidEncodingType - = (find(AUDIO_SUPPORTED_ENCODING_TYPES.begin(), AUDIO_SUPPORTED_ENCODING_TYPES.end(), encodingType) - != AUDIO_SUPPORTED_ENCODING_TYPES.end()); - AUDIO_DEBUG_LOG("AudioStream: IsEncodingTypeValid: %{public}s", isValidEncodingType ? "true" : "false"); - return isValidEncodingType; -} - -bool IsSamplingRateValid(uint32_t samplingRate) -{ - bool isValidSamplingRate - = (find(AUDIO_SUPPORTED_SAMPLING_RATES.begin(), AUDIO_SUPPORTED_SAMPLING_RATES.end(), samplingRate) - != AUDIO_SUPPORTED_SAMPLING_RATES.end()); - AUDIO_DEBUG_LOG("AudioStream: IsSamplingRateValid: %{public}s", isValidSamplingRate ? "true" : "false"); - return isValidSamplingRate; -} - -bool IsRendererChannelLayoutValid(uint64_t channelLayout) -{ - bool isValidRendererChannelLayout = (find(RENDERER_SUPPORTED_CHANNELLAYOUTS.begin(), - RENDERER_SUPPORTED_CHANNELLAYOUTS.end(), channelLayout) != RENDERER_SUPPORTED_CHANNELLAYOUTS.end()); - AUDIO_DEBUG_LOG("AudioStream: isValidRendererChannelLayout: %{public}s", - isValidRendererChannelLayout ? "true" : "false"); - return isValidRendererChannelLayout; -} - -bool IsPlaybackChannelRelatedInfoValid(uint8_t channels, uint64_t channelLayout) -{ - if (!IsRendererChannelValid(channels)) { - AUDIO_ERR_LOG("AudioStream: Invalid sink channel %{public}d", channels); - return false; - } - if (!IsRendererChannelLayoutValid(channelLayout)) { - AUDIO_ERR_LOG("AudioStream: Invalid sink channel layout"); - return false; - } - return true; -} - int32_t AudioStream::GetAudioStreamInfo(AudioStreamParams &audioStreamInfo) { AUDIO_INFO_LOG("AudioStream: GetAudioStreamInfo"); @@ -372,14 +308,16 @@ int32_t AudioStream::SetAudioStreamInfo(const AudioStreamParams info, StopAudioStream(); ReleaseAudioStream(false); } - + AUDIO_ERR_LOG("0000000000000000000000111"); AudioStreamParams param = info; int32_t ret = 0; if ((ret = InitFromParams(param)) != SUCCESS) { + AUDIO_ERR_LOG("InitFromParams error"); return ret; } + AUDIO_ERR_LOG("000000000000000000000011122"); if (CreateStream(param, eStreamType_) != SUCCESS) { AUDIO_ERR_LOG("AudioStream:Create stream failed"); diff --git a/services/audio_service/client/src/callback_handler.cpp b/services/audio_service/client/src/callback_handler.cpp new file mode 100755 index 0000000000000000000000000000000000000000..e5419138db4e1d175c88d41279aed00b3c8f0886 --- /dev/null +++ b/services/audio_service/client/src/callback_handler.cpp @@ -0,0 +1,79 @@ +/* + * 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 "callback_handler.h" +#include "event_handler.h" +#include "event_runner.h" +#include "audio_log.h" + +namespace OHOS { +namespace AudioStandard { +using namespace std; +class CallbackHandlerInner : public CallbackHandler, public AppExecFwk::EventHandler { +public: + explicit CallbackHandlerInner(std::shared_ptr iHandler); + ~CallbackHandlerInner(); + + void SendCallbackEvent(uint32_t code, int64_t data) override; + + void ReleaseEventRunner() override; + +protected: + void ProcessEvent(const AppExecFwk::InnerEvent::Pointer &event) override; + +private: + std::weak_ptr iHandler_; +}; + +std::shared_ptr CallbackHandler::GetInstance(std::shared_ptr iHandler) +{ + return std::make_shared(iHandler); +} + +CallbackHandlerInner::CallbackHandlerInner(std::shared_ptr iHandler) + : AppExecFwk::EventHandler(AppExecFwk::EventRunner::Create("OS_AudioStateCB")) +{ + iHandler_ = iHandler; +} + +CallbackHandlerInner::~CallbackHandlerInner() +{ + AUDIO_WARNING_LOG("Destructor callback handler inner"); +} + +void CallbackHandlerInner::SendCallbackEvent(uint32_t eventCode, int64_t data) +{ + SendEvent(AppExecFwk::InnerEvent::Get(eventCode, data)); +} + +void CallbackHandlerInner::ProcessEvent(const AppExecFwk::InnerEvent::Pointer &event) +{ + uint32_t eventCode = event->GetInnerEventId(); + // LYH in plan: uint64_t or int64_t? See OnHandle(uint32_t, int64_t) + uint64_t data = event->GetParam(); + std::shared_ptr handler = iHandler_.lock(); + if (handler == nullptr) { + AUDIO_ERR_LOG("iHandler is nullptr"); + return; + } + handler->OnHandle(eventCode, data); +} + +void CallbackHandlerInner::ReleaseEventRunner() +{ + SetEventRunner(nullptr); +} +} // namespace AudioStandard +} // namespace OHOS diff --git a/services/audio_service/client/src/capturer_in_client.cpp b/services/audio_service/client/src/capturer_in_client.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a0694292b441b7e864aa35d8c64d285da330af1e --- /dev/null +++ b/services/audio_service/client/src/capturer_in_client.cpp @@ -0,0 +1,1634 @@ +/* + * 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 "capturer_in_client.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "iservice_registry.h" +#include "system_ability_definition.h" +#include "securec.h" +#include "safe_block_queue.h" + +#include "ipc_stream.h" +#include "audio_log.h" +#include "audio_errors.h" + +#include "audio_manager_base.h" +#include "audio_ring_cache.h" +#include "audio_utils.h" +#include "audio_policy_manager.h" +#include "audio_server_death_recipient.h" +#include "audio_stream_tracker.h" +#include "audio_system_manager.h" +#include "ipc_stream_listener_impl.h" +#include "ipc_stream_listener_stub.h" +#include "callback_handler.h" + +namespace OHOS { +namespace AudioStandard { +namespace { +static const size_t MAX_CLIENT_READ_SIZE = 20 * 1024 * 1024; // 20M +static const int32_t CREATE_TIMEOUT_IN_SECOND = 5; // 5S +static const int32_t OPERATION_TIMEOUT_IN_MS = 500; // 500ms +const uint64_t AUDIO_US_PER_MS = 1000; +const uint64_t AUDIO_US_PER_S = 1000000; +const uint64_t DEFAULT_BUF_DURATION_IN_USEC = 20000; // 20ms +const uint64_t MAX_BUF_DURATION_IN_USEC = 2000000; // 2S +const int64_t INVALID_FRAME_SIZE = -1; +static const int32_t SHORT_TIMEOUT_IN_MS = 20; // ms +static constexpr int CB_QUEUE_CAPACITY = 1; +} +class CapturerInClientInner : public CapturerInClient, public IStreamListener, public IHandler, + public std::enable_shared_from_this { +public: + CapturerInClientInner(AudioStreamType eStreamType, int32_t appUid); + ~CapturerInClientInner(); + + // IStreamListener + int32_t OnOperationHandled(Operation operation, int64_t result) override; + + void SetClientID(int32_t clientPid, int32_t clientUid) override; + + void SetRendererInfo(const AudioRendererInfo &rendererInfo) override; + void SetCapturerInfo(const AudioCapturerInfo &capturerInfo) override; + int32_t GetAudioStreamInfo(AudioStreamParams &info) override; + int32_t SetAudioStreamInfo(const AudioStreamParams info, + const std::shared_ptr &proxyObj) override; + bool CheckRecordingCreate(uint32_t appTokenId, uint64_t appFullTokenId, int32_t appUid, SourceType sourceType = + SOURCE_TYPE_MIC) override; + bool CheckRecordingStateChange(uint32_t appTokenId, uint64_t appFullTokenId, int32_t appUid, + AudioPermissionState state) override; + State GetState() override; + int32_t GetAudioSessionID(uint32_t &sessionID) 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; + float GetVolume() override; + int32_t SetVolume(float volume) override; + int32_t SetRenderRate(AudioRendererRate renderRate) override; + AudioRendererRate GetRenderRate() override; + int32_t SetStreamCallback(const std::shared_ptr &callback) override; + int32_t SetSpeed(float speed) override; + float GetSpeed() override; + int32_t ChangeSpeed(uint8_t *buffer, int32_t bufferSize, std::unique_ptr &outBuffer, + int32_t &outBufferSize) override; + int32_t WriteSpeedBuffer(int32_t bufferSize, uint8_t *speedBuffer, size_t speedBufferSize) override; + + // callback mode api + AudioRenderMode GetRenderMode() override; + int32_t SetRenderMode(AudioRenderMode renderMode) 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 Clear() override; + int32_t GetBufQueueState(BufferQueueState &bufState) override; + int32_t Enqueue(const BufferDesc &bufDesc) override; + + int32_t SetLowPowerVolume(float volume) override; + float GetLowPowerVolume() override; + int32_t UnsetOffloadMode() override; + int32_t SetOffloadMode(int32_t state, bool isAppBack) override; + float GetSingleStreamVolume() override; + AudioEffectMode GetAudioEffectMode() override; + int32_t SetAudioEffectMode(AudioEffectMode effectMode) override; + int64_t GetFramesRead() override; + int64_t GetFramesWritten() override; + + void SetInnerCapturerState(bool isInnerCapturer) override; + void SetWakeupCapturerState(bool isWakeupCapturer) override; + void SetPrivacyType(AudioPrivacyType privacyType) override; + void SetCapturerSource(int capturerSource) override; + + // Common APIs + bool StartAudioStream(StateChangeCmdType cmdType = CMD_FROM_CLIENT) override; + bool PauseAudioStream(StateChangeCmdType cmdType = CMD_FROM_CLIENT) override; + bool StopAudioStream() override; + bool FlushAudioStream() override; + bool ReleaseAudioStream(bool releaseRunner = true) override; + + // Playback related APIs + bool DrainAudioStream() override; + int32_t Write(uint8_t *buffer, size_t bufferSize) override; + int32_t Write(uint8_t *pcmBuffer, size_t pcmSize, uint8_t *metaBuffer, size_t metaSize) override; + void SetPreferredFrameSize(int32_t frameSize) override; + + // Recording related APIs + int32_t Read(uint8_t &buffer, size_t userSize, bool isBlockingRead) override; + + // Position and period callbacks + 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; + 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; + + uint32_t GetUnderflowCount() override; + uint32_t GetRendererSamplingRate() override; + int32_t SetRendererSamplingRate(uint32_t sampleRate) override; + int32_t SetBufferSizeInMsec(int32_t bufferSizeInMsec) override; + void SetApplicationCachePath(const std::string cachePath) override; + int32_t SetChannelBlendMode(ChannelBlendMode blendMode) override; + int32_t SetVolumeWithRamp(float volume, int32_t duration) override; + + void SetStreamTrackerState(bool trackerRegisteredState) override; + void GetSwitchInfo(IAudioStream::SwitchInfo& info) override; + + IAudioStream::StreamClass GetStreamClass() override; + + static void AudioServerDied(pid_t pid); + + void OnHandle(uint32_t code, int64_t data) override; + void InitCallbackHandler(); + + void SendCapturerMarkReachedEvent(int64_t capturerMarkPosition); + void SendCapturerPeriodReachedEvent(int64_t capturerPeriodSize); + + void HandleCapturerPositionChanges(size_t bytesRead); + void HandleStateChangeEvent(int64_t data); + void HandleCapturerMarkReachedEvent(int64_t data); + void HandleCapturerPeriodReachedEvent(int64_t data); + + static const sptr GetAudioServerProxy(); +private: + void RegisterTracker(const std::shared_ptr &proxyObj); + void UpdateTracker(const std::string &updateCase); + + int32_t DeinitIpcStream(); + + int32_t InitIpcStream(); + + const AudioProcessConfig ConstructConfig(); + + int32_t InitCacheBuffer(size_t targetSize); + int32_t InitSharedBuffer(); + int32_t FlushRingCache(); + + int32_t StateCmdTypeToParams(int64_t ¶ms, State state, StateChangeCmdType cmdType); + int32_t ParamsToStateCmdType(int64_t params, State &state, StateChangeCmdType &cmdType); + + void InitCallbackBuffer(uint64_t bufferDurationInUs); + void ReadCallbackFunc(); + // for callback mode. Check status if not running, wait for start or release. + bool WaitForRunning(); +private: + AudioStreamType eStreamType_; + int32_t appUid_; + uint32_t sessionId_; + int32_t clientUid_ = -1; + int32_t clientPid_ = -1; + + std::unique_ptr audioStreamTracker_; + bool streamTrackerRegistered_ = false; + + AudioRendererInfo rendererInfo_; // not in use + AudioCapturerInfo capturerInfo_; + + int32_t bufferSizeInMsec_ = 20; // 20ms + std::string cachePath_; + + // callback mode + AudioCaptureMode capturerMode_ = CAPTURE_MODE_NORMAL; + std::thread callbackLoop_; // thread for callback to client and write. + std::atomic cbThreadReleased_ = true; + std::mutex readCbMutex_; // lock for change or use callback + std::condition_variable cbThreadCv_; + std::shared_ptr readCb_ = nullptr; + std::mutex cbBufferMutex_; + std::unique_ptr cbBuffer_ {nullptr}; + size_t cbBufferSize_ = 0; + SafeBlockQueue cbBufferQueue_; // only one cbBuffer_ + + bool isInnerCapturer_ = false; + bool isWakeupCapturer_ = false; + + bool needSetThreadPriority_ = true; + + AudioStreamParams streamParams_; // in plan: replace it with AudioCapturerParams + + // callbacks + std::mutex streamCbMutex_; + std::weak_ptr streamCallback_; + + size_t cacheSizeInByte_ = 0; + uint32_t spanSizeInFrame_ = 0; + size_t clientSpanSizeInByte_ = 0; + size_t sizePerFrameInByte_ = 4; // 16bit 2ch as default + + // using this lock when change status_ + std::mutex statusMutex_; + std::atomic state_ = INVALID; + // for status operation wait and notify + std::mutex callServerMutex_; + std::condition_variable callServerCV_; + + Operation notifiedOperation_ = MAX_OPERATION_CODE; + int64_t notifiedResult_ = 0; + + // read data + std::mutex readDataMutex_; + std::condition_variable readDataCV_; + + uint32_t overflowCount_ = 0; + // ipc stream related + AudioProcessConfig clientConfig_; + sptr listener_ = nullptr; + sptr ipcStream_ = nullptr; + std::shared_ptr clientBuffer_ = nullptr; + + // buffer handle + std::unique_ptr ringCache_ = nullptr; + std::mutex readMutex_; // used for prevent multi thread call read + + // Mark reach and period reach callback + int64_t totalBytesRead_ = 0; + std::mutex markReachMutex_; + bool capturerMarkReached_ = false; + int64_t capturerMarkPosition_ = 0; + std::shared_ptr capturerPositionCallback_ = nullptr; + + std::mutex periodReachMutex_; + int64_t capturerPeriodSize_ = 0; + int64_t capturerPeriodRead_ = 0; + std::shared_ptr capturerPeriodPositionCallback_ = nullptr; + + // Event handler + bool runnerReleased_ = false; + std::mutex runnerMutex_; + std::shared_ptr callbackHandler_ = nullptr; + + bool paramsIsSet_ = false; + + enum { + STATE_CHANGE_EVENT = 0, + RENDERER_MARK_REACHED_EVENT, + RENDERER_PERIOD_REACHED_EVENT, + CAPTURER_PERIOD_REACHED_EVENT, + CAPTURER_MARK_REACHED_EVENT, + }; + + enum : int64_t { + HANDLER_PARAM_INVALID = -1, + HANDLER_PARAM_NEW = 0, + HANDLER_PARAM_PREPARED, + HANDLER_PARAM_RUNNING, + HANDLER_PARAM_STOPPED, + HANDLER_PARAM_RELEASED, + HANDLER_PARAM_PAUSED, + HANDLER_PARAM_STOPPING, + HANDLER_PARAM_RUNNING_FROM_SYSTEM, + HANDLER_PARAM_PAUSED_FROM_SYSTEM, + }; +}; + +std::shared_ptr CapturerInClient::GetInstance(AudioStreamType eStreamType, int32_t appUid) +{ + return std::make_shared(eStreamType, appUid); +} + +CapturerInClientInner::CapturerInClientInner(AudioStreamType eStreamType, int32_t appUid) : eStreamType_(eStreamType), + appUid_(appUid), cbBufferQueue_(CB_QUEUE_CAPACITY) +{ + AUDIO_INFO_LOG("Create with StreamType:%{public}d appUid:%{public}d ", eStreamType_, appUid_); + audioStreamTracker_ = std::make_unique(AUDIO_MODE_RECORD, appUid); + state_ = NEW; +} + +CapturerInClientInner::~CapturerInClientInner() +{ + AUDIO_INFO_LOG("~CapturerInClientInner()"); +} + +int32_t CapturerInClientInner::OnOperationHandled(Operation operation, int64_t result) +{ + // read/write operation may print many log, use debug. + if (operation == UPDATE_STREAM) { + AUDIO_DEBUG_LOG("OnOperationHandled() UPDATE_STREAM result:%{public}" PRId64".", result); + // notify write if blocked + readDataCV_.notify_all(); + return SUCCESS; + } + + if (operation == BUFFER_OVERFLOW) { + overflowCount_++; + AUDIO_WARNING_LOG("recv overflow %{public}d", overflowCount_); + // in plan next: do more to reduce overflow + readDataCV_.notify_all(); + return SUCCESS; + } + + AUDIO_INFO_LOG("OnOperationHandled() recv operation:%{public}d result:%{public}" PRId64".", operation, result); + std::unique_lock lock(callServerMutex_); + notifiedOperation_ = operation; + notifiedResult_ = result; + callServerCV_.notify_all(); + return SUCCESS; +} + +void CapturerInClientInner::SetClientID(int32_t clientPid, int32_t clientUid) +{ + AUDIO_INFO_LOG("Capturer set client PID: %{public}d, UID: %{public}d", clientPid, clientUid); + clientPid_ = clientPid; + clientUid_ = clientUid; + return; +} + +void CapturerInClientInner::SetRendererInfo(const AudioRendererInfo &rendererInfo) +{ + AUDIO_WARNING_LOG("SetRendererInfo is not supported"); + return; +} + +void CapturerInClientInner::SetCapturerInfo(const AudioCapturerInfo &capturerInfo) +{ + capturerInfo_ = capturerInfo; + AUDIO_INFO_LOG("SetCapturerInfo with SourceType %{public}d flag %{public}d", capturerInfo_.sourceType, + capturerInfo_.capturerFlags); + return; +} + +void CapturerInClientInner::RegisterTracker(const std::shared_ptr &proxyObj) +{ + if (audioStreamTracker_ && audioStreamTracker_.get() && !streamTrackerRegistered_) { + // make sure sessionId_ is set before. + AUDIO_INFO_LOG("Calling register tracker, sessionid = %{public}d", sessionId_); + AudioRegisterTrackerInfo registerTrackerInfo; + + registerTrackerInfo.sessionId = sessionId_; + registerTrackerInfo.clientPid = clientPid_; + registerTrackerInfo.state = state_; + registerTrackerInfo.rendererInfo = rendererInfo_; + registerTrackerInfo.capturerInfo = capturerInfo_; + + audioStreamTracker_->RegisterTracker(registerTrackerInfo, proxyObj); + streamTrackerRegistered_ = true; + } +} + +void CapturerInClientInner::UpdateTracker(const std::string &updateCase) +{ + if (audioStreamTracker_ && audioStreamTracker_.get()) { + AUDIO_DEBUG_LOG("Capturer:Calling Update tracker for %{public}s", updateCase.c_str()); + audioStreamTracker_->UpdateTracker(sessionId_, state_, clientPid_, rendererInfo_, capturerInfo_); + } +} + +int32_t CapturerInClientInner::SetAudioStreamInfo(const AudioStreamParams info, + const std::shared_ptr &proxyObj) +{ + AUDIO_INFO_LOG("AudioStreamInfo, Sampling rate: %{public}d, channels: %{public}d, format: %{public}d, stream type:" + " %{public}d, encoding type: %{public}d", info.samplingRate, info.channels, info.format, eStreamType_, + info.encoding); + AudioXCollie guard("CapturerInClientInner::SetAudioStreamInfo", CREATE_TIMEOUT_IN_SECOND); + if (!IsFormatValid(info.format) || !IsCapturerChannelValid(info.channels) || !IsEncodingTypeValid(info.encoding) || + !IsSamplingRateValid(info.samplingRate)) { + AUDIO_ERR_LOG("CapturerInClient: Unsupported audio parameter"); + return ERR_NOT_SUPPORTED; + } + + CHECK_AND_RETURN_RET_LOG(IAudioStream::GetByteSizePerFrame(info, sizePerFrameInByte_) == SUCCESS, + ERROR_INVALID_PARAM, "GetByteSizePerFrame failed with invalid params"); + + if (state_ != NEW) { + AUDIO_INFO_LOG("State is %{public}d, not new, release existing stream and recreate.", state_.load()); + int32_t ret = DeinitIpcStream(); + CHECK_AND_RETURN_RET_LOG(ret == SUCCESS, ret, "release existing stream failed."); + } + + streamParams_ = info; // keep it for later use + paramsIsSet_ = true; + int32_t initRet = InitIpcStream(); + CHECK_AND_RETURN_RET_LOG(initRet == SUCCESS, initRet, "Init stream failed: %{public}d", initRet); + state_ = PREPARED; + + RegisterTracker(proxyObj); + return SUCCESS; +} + +std::mutex g_serverMutex; +sptr g_ServerProxy = nullptr; +const sptr CapturerInClientInner::GetAudioServerProxy() +{ + std::lock_guard lock(g_serverMutex); + if (g_ServerProxy == nullptr) { + auto samgr = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager(); + if (samgr == nullptr) { + AUDIO_ERR_LOG("GetAudioServerProxy: get sa manager failed"); + return nullptr; + } + sptr object = samgr->GetSystemAbility(AUDIO_DISTRIBUTED_SERVICE_ID); + if (object == nullptr) { + AUDIO_ERR_LOG("GetAudioServerProxy: get audio service remote object failed"); + return nullptr; + } + g_ServerProxy = iface_cast(object); + if (g_ServerProxy == nullptr) { + AUDIO_ERR_LOG("GetAudioServerProxy: get audio service proxy failed"); + return nullptr; + } + + // register death recipent to restore proxy + sptr asDeathRecipient = new(std::nothrow) AudioServerDeathRecipient(getpid()); + if (asDeathRecipient != nullptr) { + asDeathRecipient->SetNotifyCb(std::bind(&CapturerInClientInner::AudioServerDied, + std::placeholders::_1)); + bool result = object->AddDeathRecipient(asDeathRecipient); + if (!result) { + AUDIO_ERR_LOG("GetAudioServerProxy: failed to add deathRecipient"); + } + } + } + sptr gasp = g_ServerProxy; + return gasp; +} + +void CapturerInClientInner::AudioServerDied(pid_t pid) +{ + AUDIO_INFO_LOG("audio server died clear proxy, will restore proxy in next call"); + std::lock_guard lock(g_serverMutex); + g_ServerProxy = nullptr; +} + +void CapturerInClientInner::OnHandle(uint32_t code, int64_t data) +{ + AUDIO_DEBUG_LOG("On handle event, event code: %{public}d, data: %{public}" PRIu64 "", code, data); + switch (code) { + case STATE_CHANGE_EVENT: + HandleStateChangeEvent(data); + break; + case RENDERER_MARK_REACHED_EVENT: + HandleCapturerMarkReachedEvent(data); + break; + case RENDERER_PERIOD_REACHED_EVENT: + HandleCapturerPeriodReachedEvent(data); + break; + default: + break; + } +} + +void CapturerInClientInner::HandleStateChangeEvent(int64_t data) +{ + State state = INVALID; + StateChangeCmdType cmdType = CMD_FROM_CLIENT; + ParamsToStateCmdType(data, state, cmdType); + std::unique_lock lock(streamCbMutex_); + std::shared_ptr streamCb = streamCallback_.lock(); + if (streamCb != nullptr) { + state = state != STOPPING ? state : STOPPED; // client only need STOPPED + streamCb->OnStateChange(state, cmdType); + } +} + +void CapturerInClientInner::HandleCapturerMarkReachedEvent(int64_t capturerMarkPosition) +{ + AUDIO_DEBUG_LOG("Start HandleCapturerMarkReachedEvent"); + std::unique_lock lock(markReachMutex_); + if (capturerPositionCallback_) { + capturerPositionCallback_->OnMarkReached(capturerMarkPosition); + } +} + +void CapturerInClientInner::HandleCapturerPeriodReachedEvent(int64_t capturerPeriodNumber) +{ + AUDIO_DEBUG_LOG("Start HandleCapturerPeriodReachedEvent"); + std::unique_lock lock(periodReachMutex_); + if (capturerPeriodPositionCallback_) { + capturerPeriodPositionCallback_->OnPeriodReached(capturerPeriodNumber); + } +} + +// OnCapturerMarkReach by eventHandler +void CapturerInClientInner::SendCapturerMarkReachedEvent(int64_t capturerMarkPosition) +{ + std::lock_guard runnerlock(runnerMutex_); + if (runnerReleased_) { + AUDIO_WARNING_LOG("SendCapturerMarkReachedEvent runner released"); + return; + } + callbackHandler_->SendCallbackEvent(RENDERER_MARK_REACHED_EVENT, capturerMarkPosition); +} + +// OnCapturerPeriodReach by eventHandler +void CapturerInClientInner::SendCapturerPeriodReachedEvent(int64_t capturerPeriodSize) +{ + std::lock_guard runnerlock(runnerMutex_); + if (runnerReleased_) { + AUDIO_WARNING_LOG("SendCapturerPeriodReachedEvent runner released"); + return; + } + callbackHandler_->SendCallbackEvent(RENDERER_PERIOD_REACHED_EVENT, capturerPeriodSize); +} + +int32_t CapturerInClientInner::ParamsToStateCmdType(int64_t params, State &state, StateChangeCmdType &cmdType) +{ + cmdType = CMD_FROM_CLIENT; + switch (params) { + case HANDLER_PARAM_NEW: + state = NEW; + break; + case HANDLER_PARAM_PREPARED: + state = PREPARED; + break; + case HANDLER_PARAM_RUNNING: + state = RUNNING; + break; + case HANDLER_PARAM_STOPPED: + state = STOPPED; + break; + case HANDLER_PARAM_RELEASED: + state = RELEASED; + break; + case HANDLER_PARAM_PAUSED: + state = PAUSED; + break; + case HANDLER_PARAM_STOPPING: + state = STOPPING; + break; + case HANDLER_PARAM_RUNNING_FROM_SYSTEM: + state = RUNNING; + cmdType = CMD_FROM_SYSTEM; + break; + case HANDLER_PARAM_PAUSED_FROM_SYSTEM: + state = PAUSED; + cmdType = CMD_FROM_SYSTEM; + break; + default: + state = INVALID; + break; + } + return SUCCESS; +} + +int32_t CapturerInClientInner::StateCmdTypeToParams(int64_t ¶ms, State state, StateChangeCmdType cmdType) +{ + if (cmdType == CMD_FROM_CLIENT) { + params = static_cast(state); + return SUCCESS; + } + switch (state) { + case RUNNING: + params = HANDLER_PARAM_RUNNING_FROM_SYSTEM; + break; + case PAUSED: + params = HANDLER_PARAM_PAUSED_FROM_SYSTEM; + break; + default: + params = HANDLER_PARAM_INVALID; + break; + } + return SUCCESS; +} + +void CapturerInClientInner::InitCallbackHandler() +{ + if (callbackHandler_ == nullptr) { + callbackHandler_ = CallbackHandler::GetInstance(shared_from_this()); + } +} + +// call this without lock, we should be able to call deinit in any case. +int32_t CapturerInClientInner::DeinitIpcStream() +{ + CHECK_AND_RETURN_RET_LOG(ipcStream_ != nullptr, SUCCESS, "IpcStream is already nullptr"); + ipcStream_->Release(); + // in plan: + ipcStream_ = nullptr; + ringCache_->ResetBuffer(); + return SUCCESS; +} + +const AudioProcessConfig CapturerInClientInner::ConstructConfig() +{ + AudioProcessConfig config = {}; + // in plan: get token id + config.appInfo.appPid = clientPid_; + config.appInfo.appUid = clientUid_; + + config.streamInfo.channels = static_cast(streamParams_.channels); + config.streamInfo.encoding = static_cast(streamParams_.encoding); + config.streamInfo.format = static_cast(streamParams_.format); + config.streamInfo.samplingRate = static_cast(streamParams_.samplingRate); + + config.audioMode = AUDIO_MODE_RECORD; + + config.capturerInfo = capturerInfo_; + if (capturerInfo_.capturerFlags != 0) { + AUDIO_WARNING_LOG("ConstructConfig find Capturer flag invalid:%{public}d", capturerInfo_.capturerFlags); + capturerInfo_.capturerFlags = 0; + } + + config.rendererInfo = {}; + + config.streamType = eStreamType_; + + config.isInnerCapturer = isInnerCapturer_; + config.isWakeupCapturer = isWakeupCapturer_; + + clientConfig_ = config; + return config; +} + +int32_t CapturerInClientInner::InitSharedBuffer() +{ + CHECK_AND_RETURN_RET_LOG(ipcStream_ != nullptr, ERR_OPERATION_FAILED, "InitSharedBuffer failed, null ipcStream_."); + int32_t ret = ipcStream_->ResolveBuffer(clientBuffer_); + CHECK_AND_RETURN_RET_LOG(ret == SUCCESS && clientBuffer_ != nullptr, ret, "ResolveBuffer failed:%{public}d", ret); + + uint32_t totalSizeInFrame = 0; + uint32_t byteSizePerFrame = 0; + ret = clientBuffer_->GetSizeParameter(totalSizeInFrame, spanSizeInFrame_, byteSizePerFrame); + + CHECK_AND_RETURN_RET_LOG(ret == SUCCESS && byteSizePerFrame == sizePerFrameInByte_, ret, "ResolveBuffer failed" + ":%{public}d", ret); + + clientSpanSizeInByte_ = spanSizeInFrame_ * byteSizePerFrame; + + AUDIO_INFO_LOG("totalSizeInFrame_[%{public}u] spanSizeInFrame_[%{public}u] sizePerFrameInByte_[" + "%{public}zu] clientSpanSizeInByte_[%{public}zu]", totalSizeInFrame, spanSizeInFrame_, sizePerFrameInByte_, + clientSpanSizeInByte_); + + return SUCCESS; +} + +// InitCacheBuffer should be able to modify the cache size between clientSpanSizeInByte_ and 4 * clientSpanSizeInByte_ +int32_t CapturerInClientInner::InitCacheBuffer(size_t targetSize) +{ + CHECK_AND_RETURN_RET_LOG(clientSpanSizeInByte_ != 0, ERR_OPERATION_FAILED, "clientSpanSizeInByte_ invalid"); + + AUDIO_INFO_LOG("old size:%{public}zu, new size:%{public}zu", cacheSizeInByte_, targetSize); + cacheSizeInByte_ = targetSize; + + if (ringCache_ == nullptr) { + ringCache_ = AudioRingCache::Create(cacheSizeInByte_); + } else { + OptResult result = ringCache_->ReConfig(cacheSizeInByte_, false); // false --> clear buffer + if (result.ret != OPERATION_SUCCESS) { + AUDIO_ERR_LOG("ReConfig AudioRingCache to size %{public}u failed:ret%{public}zu", result.ret, targetSize); + return ERR_OPERATION_FAILED; + } + } + + return SUCCESS; +} + +int32_t CapturerInClientInner::InitIpcStream() +{ + AUDIO_INFO_LOG("Init Ipc stream"); + AudioProcessConfig config = ConstructConfig(); + + sptr gasp = CapturerInClientInner::GetAudioServerProxy(); + CHECK_AND_RETURN_RET_LOG(gasp != nullptr, ERR_OPERATION_FAILED, "Create failed, can not get service."); + sptr ipcProxy = gasp->CreateAudioProcess(config); // in plan: add ret + CHECK_AND_RETURN_RET_LOG(ipcProxy != nullptr, ERR_OPERATION_FAILED, "failed with null ipcProxy."); + ipcStream_ = iface_cast(ipcProxy); + CHECK_AND_RETURN_RET_LOG(ipcStream_ != nullptr, ERR_OPERATION_FAILED, "failed when iface_cast."); + + // in plan: old listener_ is destoried here, will server receive dieth notify? + listener_ = sptr::MakeSptr(shared_from_this()); + int32_t ret = ipcStream_->RegisterStreamListener(listener_->AsObject()); + CHECK_AND_RETURN_RET_LOG(ret == SUCCESS, ret, "RegisterStreamListener failed:%{public}d", ret); + + ret = InitSharedBuffer(); + CHECK_AND_RETURN_RET_LOG(ret == SUCCESS, ret, "InitSharedBuffer failed:%{public}d", ret); + + ret = InitCacheBuffer(clientSpanSizeInByte_); + CHECK_AND_RETURN_RET_LOG(ret == SUCCESS, ret, "InitCacheBuffer failed:%{public}d", ret); + + ret = ipcStream_->GetAudioSessionID(sessionId_); + CHECK_AND_RETURN_RET_LOG(ret == SUCCESS, ret, "GetAudioSessionID failed:%{public}d", ret); + + InitCallbackHandler(); + return SUCCESS; +} + +int32_t CapturerInClientInner::GetAudioStreamInfo(AudioStreamParams &info) +{ + CHECK_AND_RETURN_RET_LOG(paramsIsSet_ == true, ERR_OPERATION_FAILED, "Params is not set"); + info = streamParams_; + return SUCCESS; +} + +bool CapturerInClientInner::CheckRecordingCreate(uint32_t appTokenId, uint64_t appFullTokenId, int32_t appUid, + SourceType sourceType) +{ + return AudioPolicyManager::GetInstance().CheckRecordingCreate(appTokenId, appFullTokenId, appUid, sourceType); +} + +bool CapturerInClientInner::CheckRecordingStateChange(uint32_t appTokenId, uint64_t appFullTokenId, int32_t appUid, + AudioPermissionState state) +{ + return AudioPolicyManager::GetInstance().CheckRecordingStateChange(appTokenId, appFullTokenId, appUid, state); +} + +int32_t CapturerInClientInner::GetAudioSessionID(uint32_t &sessionID) +{ + CHECK_AND_RETURN_RET_LOG((state_ != RELEASED) && (state_ != NEW), ERR_ILLEGAL_STATE, + "State error %{public}d", state_.load()); + sessionID = sessionId_; + return SUCCESS; +} + +State CapturerInClientInner::GetState() +{ + return state_; +} + +bool CapturerInClientInner::GetAudioTime(Timestamp ×tamp, Timestamp::Timestampbase base) +{ + CHECK_AND_RETURN_RET_LOG(paramsIsSet_ == true, ERR_OPERATION_FAILED, "Params is not set"); + // in plan + return false; +} + +int32_t CapturerInClientInner::GetBufferSize(size_t &bufferSize) +{ + CHECK_AND_RETURN_RET_LOG(state_ != RELEASED, ERR_ILLEGAL_STATE, "Capturer stream is released"); + bufferSize = clientSpanSizeInByte_; + if (capturerMode_ == CAPTURE_MODE_CALLBACK) { + bufferSize = cbBufferSize_; + } + AUDIO_INFO_LOG("Buffer size is %{public}zu, mode is %{public}s", bufferSize, capturerMode_ == CAPTURE_MODE_NORMAL ? + "CAPTURE_MODE_NORMAL" : "CAPTURE_MODE_CALLBACK"); + return SUCCESS; +} + +int32_t CapturerInClientInner::GetFrameCount(uint32_t &frameCount) +{ + CHECK_AND_RETURN_RET_LOG(state_ != RELEASED, ERR_ILLEGAL_STATE, "Capturer stream is released"); + CHECK_AND_RETURN_RET_LOG(sizePerFrameInByte_ != 0, ERR_ILLEGAL_STATE, "sizePerFrameInByte_ is 0!"); + frameCount = spanSizeInFrame_; + if (capturerMode_ == CAPTURE_MODE_CALLBACK) { + frameCount = cbBufferSize_ / sizePerFrameInByte_; + } + AUDIO_INFO_LOG("Frame count is %{public}u, mode is %{public}s", frameCount, capturerMode_ == CAPTURE_MODE_NORMAL ? + "CAPTURE_MODE_NORMAL" : "CAPTURE_MODE_CALLBACK"); + return SUCCESS; +} + +int32_t CapturerInClientInner::GetLatency(uint64_t &latency) +{ + // GetLatency is never called in audio_capturer.cpp + latency = 150000; // unit is us, 150000 is 150ms + return ERROR; +} + +int32_t CapturerInClientInner::SetAudioStreamType(AudioStreamType audioStreamType) +{ + AUDIO_ERR_LOG("Change stream type %{public}d to %{public}d is not supported", eStreamType_, audioStreamType); + return ERROR; +} + +int32_t CapturerInClientInner::SetVolume(float volume) +{ + AUDIO_WARNING_LOG("SetVolume is only for renderer"); + return ERROR; +} + +float CapturerInClientInner::GetVolume() +{ + AUDIO_WARNING_LOG("GetVolume is only for renderer"); + return 0.0; +} + +int32_t CapturerInClientInner::SetSpeed(float speed) +{ + AUDIO_ERR_LOG("SetSpeed is not supported"); + return ERROR; +} + +float CapturerInClientInner::GetSpeed() +{ + AUDIO_ERR_LOG("GetSpeed is not supported"); + return 1.0; +} + +int32_t CapturerInClientInner::ChangeSpeed(uint8_t *buffer, int32_t bufferSize, std::unique_ptr &outBuffer, + int32_t &outBufferSize) +{ + AUDIO_ERR_LOG("ChangeSpeed is not supported"); + return ERROR; +} + +int32_t CapturerInClientInner::WriteSpeedBuffer(int32_t bufferSize, uint8_t *speedBuffer, size_t speedBufferSize) +{ + AUDIO_ERR_LOG("Speed is not supported"); + return ERROR; +} + + +int32_t CapturerInClientInner::SetRenderRate(AudioRendererRate renderRate) +{ + AUDIO_WARNING_LOG("SetRenderRate is only for renderer"); + return ERROR; +} + +AudioRendererRate CapturerInClientInner::GetRenderRate() +{ + AUDIO_WARNING_LOG("GetRenderRate is only for renderer"); + return RENDER_RATE_NORMAL; // not supported +} + +int32_t CapturerInClientInner::SetStreamCallback(const std::shared_ptr &callback) +{ + if (callback == nullptr) { + AUDIO_ERR_LOG("SetStreamCallback failed. callback == nullptr"); + return ERR_INVALID_PARAM; + } + + std::unique_lock lock(streamCbMutex_); + streamCallback_ = callback; + lock.unlock(); + + if (state_ != PREPARED) { + return SUCCESS; + } + callbackHandler_->SendCallbackEvent(STATE_CHANGE_EVENT, PREPARED); + return SUCCESS; +} + +int32_t CapturerInClientInner::SetRenderMode(AudioRenderMode renderMode) +{ + AUDIO_WARNING_LOG("SetRenderMode is only for renderer"); + return ERROR; +} + +AudioRenderMode CapturerInClientInner::GetRenderMode() +{ + AUDIO_WARNING_LOG("GetRenderMode is only for renderer"); + return RENDER_MODE_NORMAL; // not supported +} + +int32_t CapturerInClientInner::SetRendererWriteCallback(const std::shared_ptr &callback) +{ + AUDIO_WARNING_LOG("SetRendererWriteCallback is only for renderer"); + return ERROR; +} + +void CapturerInClientInner::InitCallbackBuffer(uint64_t bufferDurationInUs) +{ + if (bufferDurationInUs > MAX_BUF_DURATION_IN_USEC) { + AUDIO_ERR_LOG("InitCallbackBuffer with invalid duration %{public}" PRIu64", use default instead.", + bufferDurationInUs); + bufferDurationInUs = DEFAULT_BUF_DURATION_IN_USEC; + } + // Calculate buffer size based on duration. + cbBufferSize_ = static_cast(bufferDurationInUs * streamParams_.samplingRate / AUDIO_US_PER_S) * + sizePerFrameInByte_; + AUDIO_INFO_LOG("InitCallbackBuffer with duration %{public}" PRIu64", size: %{public}zu", bufferDurationInUs, + cbBufferSize_); + std::lock_guard lock(cbBufferMutex_); + cbBuffer_ = std::make_unique(cbBufferSize_); + BufferDesc temp = {cbBuffer_.get(), cbBufferSize_, cbBufferSize_}; + cbBufferQueue_.Push(temp); +} + +int32_t CapturerInClientInner::SetCaptureMode(AudioCaptureMode captureMode) +{ + AUDIO_INFO_LOG("Set mode to %{public}s", captureMode == CAPTURE_MODE_NORMAL ? "CAPTURE_MODE_NORMAL" : + "CAPTURE_MODE_CALLBACK"); + if (capturerMode_ == captureMode) { + return SUCCESS; + } + + // capturerMode_ is inited as CAPTURE_MODE_NORMAL, can only be set to CAPTURE_MODE_CALLBACK. + if (capturerMode_ == CAPTURE_MODE_CALLBACK && captureMode == CAPTURE_MODE_NORMAL) { + AUDIO_ERR_LOG("Set capturer mode from callback to normal is not supported."); + return ERR_INCORRECT_MODE; + } + + // state check + if (state_ != PREPARED && state_ != NEW) { + AUDIO_ERR_LOG("Set capturer mode failed. invalid state:%{public}d", state_.load()); + return ERR_ILLEGAL_STATE; + } + capturerMode_ = captureMode; + + // init callbackLoop_ + callbackLoop_ = std::thread(&CapturerInClientInner::ReadCallbackFunc, this); + pthread_setname_np(callbackLoop_.native_handle(), "OS_AudioReadCB"); + + std::unique_lock threadStartlock(statusMutex_); + bool stopWaiting = cbThreadCv_.wait_for(threadStartlock, std::chrono::milliseconds(SHORT_TIMEOUT_IN_MS), [this] { + return cbThreadReleased_ == false; // When thread is started, cbThreadReleased_ will be false. So stop waiting. + }); + if (!stopWaiting) { + AUDIO_WARNING_LOG("Init OS_AudioReadCB thread time out"); + } + + CHECK_AND_RETURN_RET_LOG(streamParams_.samplingRate != 0, ERR_ILLEGAL_STATE, "invalid sample rate"); + + uint64_t bufferDurationInUs = spanSizeInFrame_ * AUDIO_US_PER_S / streamParams_.samplingRate; + InitCallbackBuffer(bufferDurationInUs); + return SUCCESS; +} + +AudioCaptureMode CapturerInClientInner::GetCaptureMode() +{ + AUDIO_INFO_LOG("capturer mode is %{public}s", capturerMode_ == CAPTURE_MODE_NORMAL ? "CAPTURE_MODE_NORMAL" : + "CAPTURE_MODE_CALLBACK"); + return capturerMode_; +} + +int32_t CapturerInClientInner::SetCapturerReadCallback(const std::shared_ptr &callback) +{ + CHECK_AND_RETURN_RET_LOG(callback != nullptr, ERR_INVALID_PARAM, "Invalid null callback"); + CHECK_AND_RETURN_RET_LOG(capturerMode_ == CAPTURE_MODE_CALLBACK, ERR_INCORRECT_MODE, "incorrect capturer mode"); + std::lock_guard lock(readCbMutex_); + readCb_ = callback; + return SUCCESS; +} + +bool CapturerInClientInner::WaitForRunning() +{ + Trace trace("CapturerInClientInner::WaitForRunning"); + // check capturer state_: call client write only in running else wait on statusMutex_ + std::unique_lock stateLock(statusMutex_); + if (state_ != RUNNING) { + bool stopWaiting = cbThreadCv_.wait_for(stateLock, std::chrono::milliseconds(OPERATION_TIMEOUT_IN_MS), [this] { + return state_ == RUNNING || cbThreadReleased_; + }); + if (cbThreadReleased_) { + AUDIO_INFO_LOG("CBThread end in non-running status, sessionID :%{public}d", sessionId_); + return false; + } + if (!stopWaiting) { + AUDIO_INFO_LOG("Wait timeout, current state_ is %{public}d", state_.load()); // wait 0.5s + return false; + } + } + return true; +} + +void CapturerInClientInner::ReadCallbackFunc() +{ + AUDIO_INFO_LOG("Thread start, sessionID :%{public}d", sessionId_); + cbThreadReleased_ = false; + + // Modify thread priority is not need as first call read will do these work. + cbThreadCv_.notify_one(); + + // start loop + while (!cbThreadReleased_) { + Trace traceLoop("CapturerInClientInner::WriteCallbackFunc"); + if (!WaitForRunning()) { + continue; + } + + // If client didn't call GetBufferDesc/Enqueue in OnReadData, pop will block here. + BufferDesc temp = cbBufferQueue_.Pop(); + if (temp.buffer == nullptr) { + AUDIO_WARNING_LOG("Queue pop error: get nullptr."); + break; + } + + std::unique_lock lockBuffer(cbBufferMutex_); + // call read here. + int32_t result = Read(*temp.buffer, temp.bufLength, true); // blocking read + if (result < 0 || result != cbBufferSize_) { + AUDIO_WARNING_LOG("Call read error, ret:%{public}d, cbBufferSize_:%{public}zu", result, cbBufferSize_); + } + lockBuffer.unlock(); + + // call client read + Trace traceCb("CapturerInClientInner::OnReadData"); + std::unique_lock lockCb(readCbMutex_); + if (readCb_ != nullptr) { + readCb_->OnReadData(cbBufferSize_); + } + lockCb.unlock(); + traceCb.End(); + } + AUDIO_INFO_LOG("CBThread end sessionID :%{public}d", sessionId_); +} + + +int32_t CapturerInClientInner::GetBufferDesc(BufferDesc &bufDesc) +{ + Trace trace("CapturerInClientInner::GetBufferDesc"); + if (capturerMode_ != CAPTURE_MODE_CALLBACK) { + AUDIO_ERR_LOG("Not supported. mode is not callback."); + return ERR_INCORRECT_MODE; + } + std::lock_guard lock(cbBufferMutex_); + bufDesc.buffer = cbBuffer_.get(); + bufDesc.bufLength = cbBufferSize_; + bufDesc.dataLength = cbBufferSize_; + return SUCCESS; +} + +int32_t CapturerInClientInner::GetBufQueueState(BufferQueueState &bufState) +{ + Trace trace("CapturerInClientInner::GetBufQueueState"); + if (capturerMode_ != CAPTURE_MODE_CALLBACK) { + AUDIO_ERR_LOG("Not supported, mode is not callback."); + return ERR_INCORRECT_MODE; + } + // only one buffer in queue. + bufState.numBuffers = 1; + bufState.currentIndex = 0; + return SUCCESS; +} + +int32_t CapturerInClientInner::Enqueue(const BufferDesc &bufDesc) +{ + Trace trace("CapturerInClientInner::Enqueue"); + if (capturerMode_ != CAPTURE_MODE_CALLBACK) { + AUDIO_ERR_LOG("Not supported, mode is not callback."); + return ERR_INCORRECT_MODE; + } + std::lock_guard lock(cbBufferMutex_); + + if (bufDesc.bufLength != cbBufferSize_ || bufDesc.dataLength != cbBufferSize_) { + AUDIO_ERR_LOG("Enqueue invalid bufLength:%{public}zu or dataLength:%{public}zu, should be %{public}zu", + bufDesc.bufLength, bufDesc.dataLength, cbBufferSize_); + return ERR_INVALID_INDEX; + } + if (bufDesc.buffer != cbBuffer_.get()) { + AUDIO_WARNING_LOG("Enqueue buffer is not from us."); + } + + // if Enqueue is not called in OnReadData, loop thread will block on pop, wait for the Push call here. + BufferDesc temp = {cbBuffer_.get(), cbBufferSize_, cbBufferSize_}; + cbBufferQueue_.Push(temp); + // Call read may block, so put it in loop callbackLoop_ + return SUCCESS; +} + +int32_t CapturerInClientInner::Clear() +{ + Trace trace("CapturerInClientInner::Clear"); + if (capturerMode_ != CAPTURE_MODE_CALLBACK) { + AUDIO_ERR_LOG("Not supported, mode is not callback."); + return ERR_INCORRECT_MODE; + } + std::lock_guard lock(cbBufferMutex_); + int32_t ret = memset_s(cbBuffer_.get(), cbBufferSize_, 0, cbBufferSize_); + CHECK_AND_RETURN_RET_LOG(ret == EOK, ERR_OPERATION_FAILED, "Clear buffer fail, ret %{public}d.", ret); + return SUCCESS; +} + +int32_t CapturerInClientInner::SetLowPowerVolume(float volume) +{ + AUDIO_WARNING_LOG("SetLowPowerVolume is only for renderer"); + return ERROR; +} + +float CapturerInClientInner::GetLowPowerVolume() +{ + AUDIO_WARNING_LOG("GetLowPowerVolume is only for renderer"); + return 0.0; +} + +int32_t CapturerInClientInner::SetOffloadMode(int32_t state, bool isAppBack) +{ + AUDIO_WARNING_LOG("SetOffloadMode is only for renderer"); + return ERROR; +} + +int32_t CapturerInClientInner::UnsetOffloadMode() +{ + AUDIO_WARNING_LOG("UnsetOffloadMode is only for renderer"); + return ERROR; +} + +float CapturerInClientInner::GetSingleStreamVolume() +{ + AUDIO_WARNING_LOG("GetSingleStreamVolume is only for renderer"); + return 0.0; +} + +AudioEffectMode CapturerInClientInner::GetAudioEffectMode() +{ + AUDIO_WARNING_LOG("GetAudioEffectMode is only for renderer"); + return EFFECT_NONE; +} + +int32_t CapturerInClientInner::SetAudioEffectMode(AudioEffectMode effectMode) +{ + AUDIO_WARNING_LOG("SetAudioEffectMode is only for renderer"); + return ERROR; +} + +int64_t CapturerInClientInner::GetFramesWritten() +{ + AUDIO_WARNING_LOG("GetFramesWritten is only for renderer"); + return -1; +} + +int64_t CapturerInClientInner::GetFramesRead() +{ + CHECK_AND_RETURN_RET_LOG(sizePerFrameInByte_ != 0, INVALID_FRAME_SIZE, "sizePerFrameInByte_ is 0!"); + int64_t readFrameNumber = totalBytesRead_ / sizePerFrameInByte_; + return readFrameNumber; +} + +// Will only take effect before SetAudioStreaminfo +void CapturerInClientInner::SetInnerCapturerState(bool isInnerCapturer) +{ + isInnerCapturer_ = isInnerCapturer; + AUDIO_INFO_LOG("SetInnerCapturerState %{public}s", (isInnerCapturer_ ? "true" : "false")); + return; +} + +// Will only take effect before SetAudioStreaminfo +void CapturerInClientInner::SetWakeupCapturerState(bool isWakeupCapturer) +{ + isWakeupCapturer_ = isWakeupCapturer; + AUDIO_INFO_LOG("SetWakeupCapturerState %{public}s", (isWakeupCapturer_ ? "true" : "false")); + return; +} + +void CapturerInClientInner::SetCapturerSource(int capturerSource) +{ + // capturerSource is kept in capturerInfo_, no need to be set again. + (void)capturerSource; + return; +} + +void CapturerInClientInner::SetPrivacyType(AudioPrivacyType privacyType) +{ + AUDIO_WARNING_LOG("SetPrivacyType is only for renderer"); + return; +} + +bool CapturerInClientInner::StartAudioStream(StateChangeCmdType cmdType) +{ + Trace trace("CapturerInClientInner::StartAudioStream " + std::to_string(sessionId_)); + std::unique_lock statusLock(statusMutex_); + if (state_ != PREPARED && state_ != STOPPED && state_ != PAUSED) { + AUDIO_ERR_LOG("Start failed Illegal state: %{public}d", state_.load()); + return false; + } + + CHECK_AND_RETURN_RET_LOG(ipcStream_ != nullptr, false, "ipcStream is not inited!"); + int32_t ret = ipcStream_->Start(); + if (ret != SUCCESS) { + AUDIO_ERR_LOG("Start call server failed: %{public}u", ret); + return false; + } + std::unique_lock waitLock(callServerMutex_); + bool stopWaiting = callServerCV_.wait_for(waitLock, std::chrono::milliseconds(OPERATION_TIMEOUT_IN_MS), [this] { + return notifiedOperation_ == START_STREAM; // will be false when got notified. + }); + + if (notifiedOperation_ != START_STREAM || notifiedResult_ != SUCCESS) { + AUDIO_ERR_LOG("Start failed: %{public}s Operation:%{public}d result:%{public}" PRId64".", + (!stopWaiting ? "timeout" : "no timeout"), notifiedOperation_, notifiedResult_); + return false; + } + waitLock.unlock(); + + state_ = RUNNING; // change state_ to RUNNING, then notify cbThread + if (capturerMode_ == CAPTURE_MODE_CALLBACK) { + // start the callback-write thread + cbThreadCv_.notify_all(); + } + statusLock.unlock(); + // in plan: call HiSysEventWrite + int64_t param = -1; + StateCmdTypeToParams(param, state_, cmdType); + callbackHandler_->SendCallbackEvent(STATE_CHANGE_EVENT, param); + + AUDIO_INFO_LOG("Start SUCCESS, sessionId: %{public}d, uid: %{public}d", sessionId_, clientUid_); + UpdateTracker("RUNNING"); + return true; +} + +bool CapturerInClientInner::PauseAudioStream(StateChangeCmdType cmdType) +{ + Trace trace("CapturerInClientInner::PauseAudioStream " + std::to_string(sessionId_)); + std::unique_lock statusLock(statusMutex_); + if (state_ != RUNNING) { + AUDIO_ERR_LOG("Pause State is not RUNNING. Illegal state:%{public}u", state_.load()); + return false; + } + + CHECK_AND_RETURN_RET_LOG(ipcStream_ != nullptr, false, "ipcStream is not inited!"); + int32_t ret = ipcStream_->Pause(); + if (ret != SUCCESS) { + AUDIO_ERR_LOG("Pause call server failed:%{public}u", ret); + return false; + } + std::unique_lock waitLock(callServerMutex_); + bool stopWaiting = callServerCV_.wait_for(waitLock, std::chrono::milliseconds(OPERATION_TIMEOUT_IN_MS), [this] { + return notifiedOperation_ == PAUSE_STREAM; // will be false when got notified. + }); + + if (notifiedOperation_ != PAUSE_STREAM || notifiedResult_ != SUCCESS) { + AUDIO_ERR_LOG("Pause failed: %{public}s Operation:%{public}d result:%{public}" PRId64".", + (!stopWaiting ? "timeout" : "no timeout"), notifiedOperation_, notifiedResult_); + return false; + } + waitLock.unlock(); + + state_ = PAUSED; + statusLock.unlock(); + + // waiting for review: use send event to clent with cmdType | call OnStateChange | call HiSysEventWrite + int64_t param = -1; + StateCmdTypeToParams(param, state_, cmdType); + callbackHandler_->SendCallbackEvent(STATE_CHANGE_EVENT, param); + + AUDIO_INFO_LOG("Pause SUCCESS, sessionId: %{public}d, uid: %{public}d, mode %{public}s", sessionId_, clientUid_, + capturerMode_ == CAPTURE_MODE_NORMAL ? "CAPTURE_MODE_NORMAL" : "CAPTURE_MODE_CALLBACK"); + UpdateTracker("PAUSED"); + return true; +} + +bool CapturerInClientInner::StopAudioStream() +{ + Trace trace("CapturerInClientInner::StopAudioStream " + std::to_string(sessionId_)); + AUDIO_INFO_LOG("Stop begin for sessionId %{public}d uid: %{public}d", sessionId_, clientUid_); + std::unique_lock statusLock(statusMutex_); + if ((state_ != RUNNING) && (state_ != PAUSED)) { + AUDIO_ERR_LOG("Stop failed. Illegal state:%{public}u", state_.load()); + return false; + } + + CHECK_AND_RETURN_RET_LOG(ipcStream_ != nullptr, false, "ipcStream is not inited!"); + int32_t ret = ipcStream_->Stop(); + if (ret != SUCCESS) { + AUDIO_ERR_LOG("Stop call server failed:%{public}u", ret); + return false; + } + + if (capturerMode_ == CAPTURE_MODE_CALLBACK) { + state_ = STOPPING; + AUDIO_INFO_LOG("Stop begin in callback mode sessionId %{public}d uid: %{public}d", sessionId_, clientUid_); + } + + std::unique_lock waitLock(callServerMutex_); + bool stopWaiting = callServerCV_.wait_for(waitLock, std::chrono::milliseconds(OPERATION_TIMEOUT_IN_MS), [this] { + return notifiedOperation_ == STOP_STREAM; // will be false when got notified. + }); + + if (notifiedOperation_ != STOP_STREAM || notifiedResult_ != SUCCESS) { + AUDIO_ERR_LOG("Stop failed: %{public}s Operation:%{public}d result:%{public}" PRId64".", + (!stopWaiting ? "timeout" : "no timeout"), notifiedOperation_, notifiedResult_); + state_ = INVALID; + return false; + } + waitLock.unlock(); + + state_ = STOPPED; + statusLock.unlock(); + + // waiting for review: use send event to clent with cmdType | call OnStateChange | call HiSysEventWrite + callbackHandler_->SendCallbackEvent(STATE_CHANGE_EVENT, state_); + + AUDIO_INFO_LOG("Stop SUCCESS, sessionId: %{public}d, uid: %{public}d", sessionId_, clientUid_); + UpdateTracker("STOPPED"); + return true; +} + +bool CapturerInClientInner::ReleaseAudioStream(bool releaseRunner) +{ + CHECK_AND_RETURN_RET_LOG(state_ != RELEASED, ERR_ILLEGAL_STATE, "Capturer stream is already released"); + Trace trace("CapturerInClientInner::ReleaseAudioStream " + std::to_string(sessionId_)); + CHECK_AND_RETURN_RET_LOG(ipcStream_ != nullptr, false, "ipcStream is null"); + ipcStream_->Release(); + // lock_guard lock(statusMutex_) // no lock, call release in any case, include blocked case. + { + std::lock_guard runnerlock(runnerMutex_); + if (releaseRunner) { + AUDIO_INFO_LOG("runner remove"); + callbackHandler_->ReleaseEventRunner(); + runnerReleased_ = true; + } + } + + // clear write callback + if (capturerMode_ == CAPTURE_MODE_CALLBACK) { + cbThreadReleased_ = true; // stop loop + if (cbBufferQueue_.IsEmpty()) { + cbBufferQueue_.PushNoWait({nullptr, 0, 0}); + } + cbThreadCv_.notify_all(); + if (callbackLoop_.joinable()) { + callbackLoop_.join(); + } + } + paramsIsSet_ = false; + state_ = RELEASED; + UpdateTracker("RELEASED"); + AUDIO_INFO_LOG("Release end, sessionId: %{public}d, uid: %{public}d", sessionId_, clientUid_); + return true; +} + +bool CapturerInClientInner::FlushAudioStream() +{ + Trace trace("CapturerInClientInner::FlushAudioStream " + std::to_string(sessionId_)); + std::unique_lock statusLock(statusMutex_); + if ((state_ != RUNNING) && (state_ != PAUSED) && (state_ != STOPPED)) { + AUDIO_ERR_LOG("Flush failed. Illegal state:%{public}u", state_.load()); + return false; + } + CHECK_AND_RETURN_RET_LOG(FlushRingCache() == SUCCESS, false, "Flush cache failed"); + + CHECK_AND_RETURN_RET_LOG(ipcStream_ != nullptr, false, "ipcStream is not inited!"); + int32_t ret = ipcStream_->Flush(); + if (ret != SUCCESS) { + AUDIO_ERR_LOG("Flush call server failed:%{public}u", ret); + return false; + } + std::unique_lock waitLock(callServerMutex_); + bool stopWaiting = callServerCV_.wait_for(waitLock, std::chrono::milliseconds(OPERATION_TIMEOUT_IN_MS), [this] { + return notifiedOperation_ == FLUSH_STREAM; // will be false when got notified. + }); + + if (notifiedOperation_ != FLUSH_STREAM || notifiedResult_ != SUCCESS) { + AUDIO_ERR_LOG("Flush failed: %{public}s Operation:%{public}d result:%{public}" PRId64".", + (!stopWaiting ? "timeout" : "no timeout"), notifiedOperation_, notifiedResult_); + return false; + } + waitLock.unlock(); + AUDIO_INFO_LOG("Flush stream SUCCESS, sessionId: %{public}d", sessionId_); + return true; +} + +int32_t CapturerInClientInner::FlushRingCache() +{ + ringCache_->ResetBuffer(); + return SUCCESS; +} + +bool CapturerInClientInner::DrainAudioStream() +{ + AUDIO_ERR_LOG("Drain is not supported"); + return false; +} + +void CapturerInClientInner::SetPreferredFrameSize(int32_t frameSize) +{ + AUDIO_WARNING_LOG("Not Supported Yet"); +} + +int32_t CapturerInClientInner::Write(uint8_t *pcmBuffer, size_t pcmBufferSize, uint8_t *metaBuffer, + size_t metaBufferSize) +{ + AUDIO_ERR_LOG("Write is not supported"); + return ERR_INVALID_OPERATION; +} + +int32_t CapturerInClientInner::Write(uint8_t *buffer, size_t bufferSize) +{ + AUDIO_ERR_LOG("Write is not supported"); + return ERR_INVALID_OPERATION; +} + +int32_t CapturerInClientInner::Read(uint8_t &buffer, size_t userSize, bool isBlockingRead) +{ + Trace trace("CapturerInClientInner::Read " + std::to_string(userSize)); + + CHECK_AND_RETURN_RET_LOG(userSize < MAX_CLIENT_READ_SIZE, ERR_INVALID_PARAM, "invalid size %{public}zu", userSize); + + std::lock_guard lock(readMutex_); + + // if first call, call set thread priority. if thread tid change recall set thread priority + if (needSetThreadPriority_) { + AudioSystemManager::GetInstance()->RequestThreadPriority(gettid()); + needSetThreadPriority_ = false; + } + + std::unique_lock statusLock(statusMutex_); // status check + CHECK_AND_RETURN_RET_LOG(state_ == RUNNING, ERR_ILLEGAL_STATE, "Illegal state:%{public}u", state_.load()); + statusLock.unlock(); + size_t readSize = 0; + while (readSize < userSize) { + AUDIO_DEBUG_LOG("readSize %{public}zu < userSize %{public}zu", readSize, userSize); + OptResult result = ringCache_->GetReadableSize(); + CHECK_AND_RETURN_RET_LOG(result.ret == OPERATION_SUCCESS, ERROR, "GetReadableSize err %{public}d", result.ret); + size_t readableSize = std::min(result.size, userSize - readSize); + if (readSize + result.size >= userSize) { // If ringCache is sufficient + result = ringCache_->Dequeue({&buffer + (readSize), readableSize}); + CHECK_AND_RETURN_RET_LOG(result.ret == OPERATION_SUCCESS, ERROR, "ringCache Dequeue failed %{public}d", + result.ret); + readSize += readableSize; + return readSize; // data size + } + + if (result.size != 0) { + result = ringCache_->Dequeue({&buffer + readSize, result.size}); + CHECK_AND_RETURN_RET_LOG(result.ret == OPERATION_SUCCESS, ERROR, "Dequeue failed %{public}d", result.ret); + readSize += result.size; + } + uint64_t availableSizeInFrame = clientBuffer_->GetCurWriteFrame() - clientBuffer_->GetCurReadFrame(); + AUDIO_DEBUG_LOG("availableSizeInFrame %{public}" PRId64 "", availableSizeInFrame); + if (availableSizeInFrame > 0) { // If OHAudioBuffer has data + BufferDesc currentOHBuffer_ = {}; + clientBuffer_->GetReadbuffer(clientBuffer_->GetCurReadFrame(), currentOHBuffer_); + BufferWrap bufferWrap = {currentOHBuffer_.buffer, clientSpanSizeInByte_}; + ringCache_->Enqueue(bufferWrap); + clientBuffer_->SetCurReadFrame(clientBuffer_->GetCurReadFrame() + spanSizeInFrame_); + } else { + if (!isBlockingRead) { + return readSize; // Return buffer immediately + } + // wait for server read some data + std::unique_lock lock(readDataMutex_); + std::cv_status stat = readDataCV_.wait_for(lock, std::chrono::milliseconds(OPERATION_TIMEOUT_IN_MS)); + CHECK_AND_RETURN_RET_LOG(stat == std::cv_status::no_timeout, ERROR, "write data time out"); + } + } + return readSize; +} + +void CapturerInClientInner::HandleCapturerPositionChanges(size_t bytesRead) +{ + totalBytesRead_ += bytesRead; + if (sizePerFrameInByte_ == 0) { + AUDIO_ERR_LOG("HandleCapturerPositionChanges: sizePerFrameInByte_ is 0"); + return; + } + int64_t readFrameNumber = totalBytesRead_ / sizePerFrameInByte_; + AUDIO_DEBUG_LOG("totalBytesRead_ %{public}" PRId64 ", frame size: %{public}zu", totalBytesRead_, + sizePerFrameInByte_); + { + std::lock_guard lock(markReachMutex_); + if (!capturerMarkReached_) { + AUDIO_DEBUG_LOG("Frame mark position: %{public}" PRId64 ", Total frames read: %{public}" PRId64, + static_cast(capturerMarkPosition_), static_cast(readFrameNumber)); + if (readFrameNumber >= capturerMarkPosition_) { + AUDIO_DEBUG_LOG("capturerInClient OnMarkReached"); + SendCapturerMarkReachedEvent(capturerMarkPosition_); + capturerMarkReached_ = true; + } + } + } + + { + std::lock_guard lock(periodReachMutex_); + capturerPeriodRead_ += (totalBytesRead_ / sizePerFrameInByte_); + AUDIO_DEBUG_LOG("Frame period number: %{public}" PRId64 ", Total frames written: %{public}" PRId64, + static_cast(capturerPeriodSize_), static_cast(capturerPeriodRead_)); + if (capturerPeriodRead_ >= capturerPeriodSize_) { + capturerPeriodRead_ %= capturerPeriodSize_; + AUDIO_DEBUG_LOG("OnPeriodReached, remaining frames: %{public}" PRId64, + static_cast(capturerPeriodRead_)); + SendCapturerPeriodReachedEvent(capturerPeriodSize_); + } + } +} + +uint32_t CapturerInClientInner::GetUnderflowCount() +{ + // not supported for capturer + AUDIO_WARNING_LOG("No Underflow in Capturer"); + return 0; +} + +void CapturerInClientInner::SetCapturerPositionCallback(int64_t markPosition, const + std::shared_ptr &callback) +{ + std::lock_guard lock(markReachMutex_); + CHECK_AND_RETURN_LOG(callback != nullptr, "CapturerPositionCallback is nullptr"); + capturerPositionCallback_ = callback; + capturerMarkPosition_ = markPosition; + capturerMarkReached_ = false; +} + +void CapturerInClientInner::UnsetCapturerPositionCallback() +{ + std::lock_guard lock(markReachMutex_); + capturerPositionCallback_ = nullptr; + capturerMarkPosition_ = 0; + capturerMarkReached_ = false; +} + +void CapturerInClientInner::SetCapturerPeriodPositionCallback(int64_t markPosition, + const std::shared_ptr &callback) +{ + std::lock_guard lock(periodReachMutex_); + CHECK_AND_RETURN_LOG(callback != nullptr, "CapturerPeriodPositionCallback is nullptr"); + capturerPeriodPositionCallback_ = callback; + capturerPeriodSize_ = 0; + totalBytesRead_ = 0; + capturerPeriodRead_ = 0; +} + +void CapturerInClientInner::UnsetCapturerPeriodPositionCallback() +{ + std::lock_guard lock(periodReachMutex_); + capturerPeriodPositionCallback_ = nullptr; + capturerPeriodSize_ = 0; + totalBytesRead_ = 0; + capturerPeriodRead_ = 0; +} + +void CapturerInClientInner::SetRendererPositionCallback(int64_t markPosition, + const std::shared_ptr &callback) +{ + AUDIO_ERR_LOG("SetRendererPositionCallback is not supported"); + return; +} + +void CapturerInClientInner::UnsetRendererPositionCallback() +{ + AUDIO_ERR_LOG("UnsetRendererPositionCallback is not supported"); + return; +} + +void CapturerInClientInner::SetRendererPeriodPositionCallback(int64_t markPosition, + const std::shared_ptr &callback) +{ + AUDIO_ERR_LOG("SetRendererPeriodPositionCallback is not supported"); + return; +} + +void CapturerInClientInner::UnsetRendererPeriodPositionCallback() +{ + AUDIO_ERR_LOG("UnsetRendererPeriodPositionCallback is not supported"); + return; +} + +int32_t CapturerInClientInner::SetRendererSamplingRate(uint32_t sampleRate) +{ + // in plan + return ERROR; +} + +uint32_t CapturerInClientInner::GetRendererSamplingRate() +{ + // in plan + return 0; // not supported +} + +int32_t CapturerInClientInner::SetBufferSizeInMsec(int32_t bufferSizeInMsec) +{ + // bufferSizeInMsec is checked between 5ms and 20ms. + bufferSizeInMsec_ = bufferSizeInMsec; + AUDIO_INFO_LOG("SetBufferSizeInMsec to %{publid}d", bufferSizeInMsec_); + if (capturerMode_ == CAPTURE_MODE_CALLBACK) { + uint64_t bufferDurationInUs = bufferSizeInMsec_ * AUDIO_US_PER_MS; + InitCallbackBuffer(bufferDurationInUs); + } + return SUCCESS; +} + +void CapturerInClientInner::SetApplicationCachePath(const std::string cachePath) +{ + cachePath_ = cachePath; + AUDIO_INFO_LOG("SetApplicationCachePath to %{publid}s", cachePath_.c_str()); +} + +int32_t CapturerInClientInner::SetChannelBlendMode(ChannelBlendMode blendMode) +{ + AUDIO_WARNING_LOG("not supported in capturer"); + return ERROR; +} + +int32_t CapturerInClientInner::SetVolumeWithRamp(float volume, int32_t duration) +{ + AUDIO_WARNING_LOG("not supported in capturer"); + return ERROR; +} + +void CapturerInClientInner::SetStreamTrackerState(bool trackerRegisteredState) +{ + streamTrackerRegistered_ = trackerRegisteredState; +} + +void CapturerInClientInner::GetSwitchInfo(IAudioStream::SwitchInfo& info) +{ + // in plan +} + +IAudioStream::StreamClass CapturerInClientInner::GetStreamClass() +{ + return PA_STREAM; +} +} // namespace AudioStandard +} // namespace OHOS +#endif // FAST_AUDIO_STREAM_H diff --git a/services/audio_service/client/src/i_audio_stream.cpp b/services/audio_service/client/src/i_audio_stream.cpp index f168c18c93de7ec8c4fbbd42647c756a6d83b50f..736281d649579d267f86433e14f7ca8dfcf6af33 100644 --- a/services/audio_service/client/src/i_audio_stream.cpp +++ b/services/audio_service/client/src/i_audio_stream.cpp @@ -16,10 +16,15 @@ #include "i_audio_stream.h" #include +#include "audio_errors.h" #include "audio_log.h" +#include "audio_utils.h" #include "audio_stream.h" #include "fast_audio_stream.h" +#include "capturer_in_client.h" +#include "renderer_in_client.h" + namespace OHOS { namespace AudioStandard { const std::map, AudioStreamType> streamTypeMap_ = IAudioStream::CreateStreamMap(); @@ -126,6 +131,38 @@ const std::string IAudioStream::GetEffectSceneName(AudioStreamType audioType) return sceneName; } +int32_t IAudioStream::GetByteSizePerFrame(const AudioStreamParams ¶ms, size_t &result) +{ + result = 0; + size_t bitWidthSize = 0; + switch (params.format) { + case SAMPLE_U8: + bitWidthSize = 1; // size is 1 + break; + case SAMPLE_S16LE: + bitWidthSize = 2; // size is 2 + break; + case SAMPLE_S24LE: + bitWidthSize = 3; // size is 3 + break; + case SAMPLE_S32LE: + bitWidthSize = 4; // size is 4 + break; + case SAMPLE_F32LE: + bitWidthSize = 4; // size is 4 + break; + default: + return ERR_INVALID_PARAM; + break; + } + + if (params.channels < 1 || params.channels > 16) { // 1 is min channel size, 16 is max channel size + return ERR_INVALID_PARAM; + } + result = bitWidthSize * static_cast(params.channels); + return SUCCESS; +} + bool IAudioStream::IsStreamSupported(int32_t streamFlags, const AudioStreamParams ¶ms) { // 0 for normal stream @@ -169,7 +206,15 @@ std::shared_ptr IAudioStream::GetPlaybackStream(StreamClass stream AUDIO_INFO_LOG("Create fast playback stream"); return std::make_shared(eStreamType, AUDIO_MODE_PLAYBACK, appUid); } + + int32_t ipcFlag = 0; + if (GetSysPara("persist.multimedia.audio.stream.ipc", ipcFlag) && ipcFlag == 1) { + AUDIO_INFO_LOG("Create ipc playback stream"); + return RendererInClient::GetInstance(eStreamType, appUid); + } + if (streamClass == PA_STREAM) { + AUDIO_INFO_LOG("Create pa playback stream"); return std::make_shared(eStreamType, AUDIO_MODE_PLAYBACK, appUid); } return nullptr; @@ -183,10 +228,82 @@ std::shared_ptr IAudioStream::GetRecordStream(StreamClass streamCl AUDIO_INFO_LOG("Create fast record stream"); return std::make_shared(eStreamType, AUDIO_MODE_RECORD, appUid); } + + int32_t ipcFlag = 0; + if (GetSysPara("persist.multimedia.audio.stream.ipc", ipcFlag) && ipcFlag == 1) { + AUDIO_INFO_LOG("Create ipc record stream"); + return CapturerInClient::GetInstance(eStreamType, appUid); + } + if (streamClass == PA_STREAM) { + AUDIO_INFO_LOG("Create pa record stream"); return std::make_shared(eStreamType, AUDIO_MODE_RECORD, appUid); } return nullptr; } + +bool IAudioStream::IsFormatValid(uint8_t format) +{ + bool isValidFormat = (find(AUDIO_SUPPORTED_FORMATS.begin(), AUDIO_SUPPORTED_FORMATS.end(), format) + != AUDIO_SUPPORTED_FORMATS.end()); + AUDIO_DEBUG_LOG("AudioStream: IsFormatValid: %{public}s", isValidFormat ? "true" : "false"); + return isValidFormat; +} + +bool IAudioStream::IsRendererChannelValid(uint8_t channel) +{ + bool isValidChannel = (find(RENDERER_SUPPORTED_CHANNELS.begin(), RENDERER_SUPPORTED_CHANNELS.end(), channel) + != RENDERER_SUPPORTED_CHANNELS.end()); + AUDIO_DEBUG_LOG("AudioStream: IsChannelValid: %{public}s", isValidChannel ? "true" : "false"); + return isValidChannel; +} + +bool IAudioStream::IsCapturerChannelValid(uint8_t channel) +{ + bool isValidChannel = (find(CAPTURER_SUPPORTED_CHANNELS.begin(), CAPTURER_SUPPORTED_CHANNELS.end(), channel) + != CAPTURER_SUPPORTED_CHANNELS.end()); + AUDIO_DEBUG_LOG("AudioStream: IsChannelValid: %{public}s", isValidChannel ? "true" : "false"); + return isValidChannel; +} + +bool IAudioStream::IsEncodingTypeValid(uint8_t encodingType) +{ + bool isValidEncodingType + = (find(AUDIO_SUPPORTED_ENCODING_TYPES.begin(), AUDIO_SUPPORTED_ENCODING_TYPES.end(), encodingType) + != AUDIO_SUPPORTED_ENCODING_TYPES.end()); + AUDIO_DEBUG_LOG("AudioStream: IsEncodingTypeValid: %{public}s", isValidEncodingType ? "true" : "false"); + return isValidEncodingType; +} + +bool IAudioStream::IsSamplingRateValid(uint32_t samplingRate) +{ + bool isValidSamplingRate + = (find(AUDIO_SUPPORTED_SAMPLING_RATES.begin(), AUDIO_SUPPORTED_SAMPLING_RATES.end(), samplingRate) + != AUDIO_SUPPORTED_SAMPLING_RATES.end()); + AUDIO_DEBUG_LOG("AudioStream: IsSamplingRateValid: %{public}s", isValidSamplingRate ? "true" : "false"); + return isValidSamplingRate; +} + +bool IAudioStream::IsRendererChannelLayoutValid(uint64_t channelLayout) +{ + bool isValidRendererChannelLayout = (find(RENDERER_SUPPORTED_CHANNELLAYOUTS.begin(), + RENDERER_SUPPORTED_CHANNELLAYOUTS.end(), channelLayout) != RENDERER_SUPPORTED_CHANNELLAYOUTS.end()); + AUDIO_DEBUG_LOG("AudioStream: isValidRendererChannelLayout: %{public}s", + isValidRendererChannelLayout ? "true" : "false"); + return isValidRendererChannelLayout; +} + +bool IAudioStream::IsPlaybackChannelRelatedInfoValid(uint8_t channels, uint64_t channelLayout) +{ + if (!IsRendererChannelValid(channels)) { + AUDIO_ERR_LOG("AudioStream: Invalid sink channel %{public}d", channels); + return false; + } + if (!IsRendererChannelLayoutValid(channelLayout)) { + AUDIO_ERR_LOG("AudioStream: Invalid sink channel layout"); + return false; + } + return true; +} } // namespace AudioStandard } // namespace OHOS diff --git a/services/audio_service/client/src/ipc_stream_listener_impl.cpp b/services/audio_service/client/src/ipc_stream_listener_impl.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7247c580209f6fabcb4b5424cb4c9207f1561818 --- /dev/null +++ b/services/audio_service/client/src/ipc_stream_listener_impl.cpp @@ -0,0 +1,42 @@ +/* + * 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 "cinttypes" +#include "ipc_stream_listener_impl.h" +#include "audio_log.h" +#include "audio_errors.h" + +namespace OHOS { +namespace AudioStandard { +IpcStreamListenerImpl::IpcStreamListenerImpl(std::shared_ptr innerListener) +{ + if (innerListener == nullptr) { + AUDIO_ERR_LOG("IpcStreamListenerImpl() find null rendererInClinet"); + } + innerListener_ = innerListener; +} + +int32_t IpcStreamListenerImpl::OnOperationHandled(Operation operation, int64_t result) +{ + std::shared_ptr listener = innerListener_.lock(); + if (listener == nullptr) { + AUDIO_WARNING_LOG("OnOperationHandled() find innerListener_ is null, operation:%{public}d result:" + "%{public}" PRId64".", operation, result); + return ERR_ILLEGAL_STATE; + } + return listener->OnOperationHandled(operation, result); +} +} // namespace AudioStandard +} // namespace OHOS diff --git a/services/audio_service/client/src/ipc_stream_listener_stub.cpp b/services/audio_service/client/src/ipc_stream_listener_stub.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6342c53c0363b77a7849644e1014bd4040e2ac02 --- /dev/null +++ b/services/audio_service/client/src/ipc_stream_listener_stub.cpp @@ -0,0 +1,61 @@ +/* + * 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 "ipc_stream_listener_stub.h" +#include "audio_log.h" +#include "audio_errors.h" + +namespace OHOS { +namespace AudioStandard { +bool IpcStreamListenerStub::CheckInterfaceToken(MessageParcel &data) +{ + static auto localDescriptor = IpcStreamListener::GetDescriptor(); + auto remoteDescriptor = data.ReadInterfaceToken(); + if (remoteDescriptor != localDescriptor) { + AUDIO_ERR_LOG("CheckInterFfaceToken failed."); + return false; + } + return true; +} + +int IpcStreamListenerStub::OnRemoteRequest(uint32_t code, MessageParcel &data, MessageParcel &reply, + MessageOption &option) +{ + if (!CheckInterfaceToken(data)) { + return AUDIO_ERR; + } + if (code >= IpcStreamListenerMsg::IPC_STREAM_LISTENER_MAX_MSG) { + AUDIO_WARNING_LOG("OnRemoteRequest unsupported request code:%{public}d.", code); + return IPCObjectStub::OnRemoteRequest(code, data, reply, option); + } + return (this->*funcList_[code])(data, reply); +} + +int32_t IpcStreamListenerStub::HandleOnOperationHandled(MessageParcel &data, MessageParcel &reply) +{ + int32_t temp = data.ReadInt32(); + if (temp < 0 || temp >= MAX_OPERATION_CODE) { + reply.WriteInt32(AUDIO_INVALID_PARAM); + AUDIO_ERR_LOG("HandleOnOperationHandled failed, invalid operation: %{public}d", temp); + return AUDIO_INVALID_PARAM; + } + + Operation operation = static_cast(temp); + int64_t result = data.ReadInt64(); + reply.WriteInt32(OnOperationHandled(operation, result)); + return AUDIO_OK; +} +} // namespace AudioStandard +} // namespace OHOS diff --git a/services/audio_service/client/src/ipc_stream_proxy.cpp b/services/audio_service/client/src/ipc_stream_proxy.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8703f3ebf4212cca19a10a784d37c50943f619b9 --- /dev/null +++ b/services/audio_service/client/src/ipc_stream_proxy.cpp @@ -0,0 +1,343 @@ +/* + * 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 "ipc_stream_proxy.h" +#include "audio_log.h" +#include "audio_errors.h" + +namespace OHOS { +namespace AudioStandard { +IpcStreamProxy::IpcStreamProxy(const sptr &impl) : IRemoteProxy(impl) +{ + AUDIO_INFO_LOG("IpcStreamProxy()"); +} + +IpcStreamProxy::~IpcStreamProxy() +{ + AUDIO_INFO_LOG("~IpcStreamProxy()"); +} + +int32_t IpcStreamProxy::RegisterStreamListener(sptr object) +{ + MessageParcel data; + MessageParcel reply; + MessageOption option; + + CHECK_AND_RETURN_RET_LOG(data.WriteInterfaceToken(GetDescriptor()), ERROR, "Write descriptor failed!"); + + if (object == nullptr) { + AUDIO_ERR_LOG("RegisterStreamListener object is null"); + return ERR_NULL_OBJECT; + } + + data.WriteRemoteObject(object); + + int ret = Remote()->SendRequest(IpcStreamMsg::ON_REGISTER_STREAM_LISTENER, data, reply, option); + CHECK_AND_RETURN_RET_LOG(ret == AUDIO_OK, ERR_OPERATION_FAILED, "RegisterStreamListener failed," + "error: %{public}d", ret); + + return reply.ReadInt32(); +} + +int32_t IpcStreamProxy::ResolveBuffer(std::shared_ptr &buffer) +{ + MessageParcel data; + MessageParcel reply; + MessageOption option; + + CHECK_AND_RETURN_RET_LOG(data.WriteInterfaceToken(GetDescriptor()), ERROR, "Write descriptor failed!"); + + int ret = Remote()->SendRequest(IpcStreamMsg::ON_RESOLVE_BUFFER, data, reply, option); + CHECK_AND_RETURN_RET_LOG((ret == AUDIO_OK && reply.ReadInt32() == AUDIO_OK), ERR_OPERATION_FAILED, + "ResolveBuffer failed, error: %{public}d", ret); + buffer = OHAudioBuffer::ReadFromParcel(reply); + CHECK_AND_RETURN_RET_LOG(buffer != nullptr, ERR_OPERATION_FAILED, "ReadFromParcel failed"); + return SUCCESS; +} + +int32_t IpcStreamProxy::UpdatePosition() +{ + MessageParcel data; + MessageParcel reply; + MessageOption option; + + CHECK_AND_RETURN_RET_LOG(data.WriteInterfaceToken(GetDescriptor()), ERROR, "Write descriptor failed!"); + + int ret = Remote()->SendRequest(IpcStreamMsg::ON_UPDATE_POSITION, data, reply, option); + CHECK_AND_RETURN_RET_LOG(ret == AUDIO_OK, ERR_OPERATION_FAILED, "UpdatePosition failed, error: %{public}d", ret); + + return reply.ReadInt32(); +} + +int32_t IpcStreamProxy::GetAudioSessionID(uint32_t &sessionId) +{ + MessageParcel data; + MessageParcel reply; + MessageOption option; + + CHECK_AND_RETURN_RET_LOG(data.WriteInterfaceToken(GetDescriptor()), ERROR, "Write descriptor failed!"); + + int ret = Remote()->SendRequest(IpcStreamMsg::ON_GET_AUDIO_SESSIONID, data, reply, option); + CHECK_AND_RETURN_RET_LOG(ret == AUDIO_OK, ERR_OPERATION_FAILED, "GetAudioSessionID failed, error: %{public}d", ret); + ret = reply.ReadInt32(); + CHECK_AND_RETURN_RET_LOG(ret == SUCCESS, ret, "GetAudioSessionID failed, error: %{public}d", ret); + sessionId = reply.ReadUint32(); + return ret; +} + +int32_t IpcStreamProxy::Start() +{ + MessageParcel data; + MessageParcel reply; + MessageOption option; + + CHECK_AND_RETURN_RET_LOG(data.WriteInterfaceToken(GetDescriptor()), ERROR, "Write descriptor failed!"); + + int ret = Remote()->SendRequest(IpcStreamMsg::ON_START, data, reply, option); + CHECK_AND_RETURN_RET_LOG(ret == AUDIO_OK, ERR_OPERATION_FAILED, "Start failed, error: %{public}d", ret); + + return reply.ReadInt32(); +} + +int32_t IpcStreamProxy::Pause() +{ + MessageParcel data; + MessageParcel reply; + MessageOption option; + + CHECK_AND_RETURN_RET_LOG(data.WriteInterfaceToken(GetDescriptor()), ERROR, "Write descriptor failed!"); + int ret = Remote()->SendRequest(IpcStreamMsg::ON_PAUSE, data, reply, option); + CHECK_AND_RETURN_RET_LOG(ret == AUDIO_OK, ERR_OPERATION_FAILED, "Pause failed, error: %{public}d", ret); + + return reply.ReadInt32(); +} + +int32_t IpcStreamProxy::Stop() +{ + MessageParcel data; + MessageParcel reply; + MessageOption option; + + CHECK_AND_RETURN_RET_LOG(data.WriteInterfaceToken(GetDescriptor()), ERROR, "Write descriptor failed!"); + + int ret = Remote()->SendRequest(IpcStreamMsg::ON_STOP, data, reply, option); + CHECK_AND_RETURN_RET_LOG(ret == AUDIO_OK, ERR_OPERATION_FAILED, "Stop failed, error: %{public}d", ret); + + return reply.ReadInt32(); +} + +int32_t IpcStreamProxy::Release() +{ + MessageParcel data; + MessageParcel reply; + MessageOption option; + + CHECK_AND_RETURN_RET_LOG(data.WriteInterfaceToken(GetDescriptor()), ERROR, "Write descriptor failed!"); + + int ret = Remote()->SendRequest(IpcStreamMsg::ON_RELEASE, data, reply, option); + CHECK_AND_RETURN_RET_LOG(ret == AUDIO_OK, ERR_OPERATION_FAILED, "Release failed, error: %{public}d", ret); + + return reply.ReadInt32(); +} + +int32_t IpcStreamProxy::Flush() +{ + MessageParcel data; + MessageParcel reply; + MessageOption option; + + CHECK_AND_RETURN_RET_LOG(data.WriteInterfaceToken(GetDescriptor()), ERROR, "Write descriptor failed!"); + + int ret = Remote()->SendRequest(IpcStreamMsg::ON_FLUSH, data, reply, option); + CHECK_AND_RETURN_RET_LOG(ret == AUDIO_OK, ERR_OPERATION_FAILED, "Flush failed, error: %{public}d", ret); + + return reply.ReadInt32(); +} + +int32_t IpcStreamProxy::Drain() +{ + MessageParcel data; + MessageParcel reply; + MessageOption option; + + CHECK_AND_RETURN_RET_LOG(data.WriteInterfaceToken(GetDescriptor()), ERROR, "Write descriptor failed!"); + + int ret = Remote()->SendRequest(IpcStreamMsg::ON_DRAIN, data, reply, option); + CHECK_AND_RETURN_RET_LOG(ret == AUDIO_OK, ret, "Drain failed, ipc error: %{public}d", ret); + return reply.ReadInt32(); +} + +int32_t IpcStreamProxy::GetAudioTime(uint64_t &framePos, uint64_t &timeStamp) +{ + MessageParcel data; + MessageParcel reply; + MessageOption option; + + CHECK_AND_RETURN_RET_LOG(data.WriteInterfaceToken(GetDescriptor()), ERROR, "Write descriptor failed!"); + + int ret = Remote()->SendRequest(IpcStreamMsg::OH_GET_AUDIO_TIME, data, reply, option); + CHECK_AND_RETURN_RET_LOG(ret == AUDIO_OK, ret, "GetAudioTime failed, ipc error: %{public}d", ret); + ret = reply.ReadInt32(); + CHECK_AND_RETURN_RET_LOG(ret == SUCCESS, ret, "GetAudioTime failed, error: %{public}d", ret); + framePos = reply.ReadUint64(); + timeStamp = reply.ReadInt64(); + return ret; +} + +int32_t IpcStreamProxy::GetLatency(uint64_t &latency) +{ + MessageParcel data; + MessageParcel reply; + MessageOption option; + + CHECK_AND_RETURN_RET_LOG(data.WriteInterfaceToken(GetDescriptor()), ERROR, "Write descriptor failed!"); + + int ret = Remote()->SendRequest(IpcStreamMsg::ON_GET_LATENCY, data, reply, option); + CHECK_AND_RETURN_RET_LOG(ret == AUDIO_OK, ret, "GetLatency failed, ipc error: %{public}d", ret); + ret = reply.ReadInt32(); + CHECK_AND_RETURN_RET_LOG(ret == SUCCESS, ret, "GetLatency failed, error: %{public}d", ret); + latency = reply.ReadUint64(); + + return ret; +} + +int32_t IpcStreamProxy::SetRate(int32_t rate) +{ + MessageParcel data; + MessageParcel reply; + MessageOption option; + + CHECK_AND_RETURN_RET_LOG(data.WriteInterfaceToken(GetDescriptor()), ERROR, "Write descriptor failed!"); + data.WriteInt32(rate); + int ret = Remote()->SendRequest(IpcStreamMsg::ON_SET_RATE, data, reply, option); + CHECK_AND_RETURN_RET_LOG(ret == AUDIO_OK, ERR_OPERATION_FAILED, "SetRate failed, ipc error: %{public}d", ret); + + return reply.ReadInt32(); +} + +int32_t IpcStreamProxy::GetRate(int32_t &rate) +{ + MessageParcel data; + MessageParcel reply; + MessageOption option; + + CHECK_AND_RETURN_RET_LOG(data.WriteInterfaceToken(GetDescriptor()), ERROR, "Write descriptor failed!"); + + int ret = Remote()->SendRequest(IpcStreamMsg::ON_GET_RATE, data, reply, option); + CHECK_AND_RETURN_RET_LOG(ret == AUDIO_OK, ret, "GetRate failed, ipc error: %{public}d", ret); + ret = reply.ReadInt32(); + CHECK_AND_RETURN_RET_LOG(ret == SUCCESS, ret, "GetRate failed, error: %{public}d", ret); + rate = reply.ReadInt32(); + + return ret; +} + +int32_t IpcStreamProxy::SetLowPowerVolume(float volume) +{ + MessageParcel data; + MessageParcel reply; + MessageOption option; + + CHECK_AND_RETURN_RET_LOG(data.WriteInterfaceToken(GetDescriptor()), ERROR, "Write descriptor failed!"); + + data.WriteFloat(volume); + int ret = Remote()->SendRequest(IpcStreamMsg::ON_SET_LOWPOWER_VOLUME, data, reply, option); + CHECK_AND_RETURN_RET_LOG(ret == AUDIO_OK, ERR_OPERATION_FAILED, "SetLowPowerVolume failed, error:%{public}d", ret); + + return reply.ReadInt32(); +} + +int32_t IpcStreamProxy::GetLowPowerVolume(float &volume) +{ + MessageParcel data; + MessageParcel reply; + MessageOption option; + + CHECK_AND_RETURN_RET_LOG(data.WriteInterfaceToken(GetDescriptor()), ERROR, "Write descriptor failed!"); + + int ret = Remote()->SendRequest(IpcStreamMsg::ON_GET_LOWPOWER_VOLUME, data, reply, option); + CHECK_AND_RETURN_RET_LOG(ret == AUDIO_OK, ret, "GetLowPowerVolume failed, ipc error: %{public}d", ret); + ret = reply.ReadInt32(); + CHECK_AND_RETURN_RET_LOG(ret == SUCCESS, ret, "GetLowPowerVolume failed, error: %{public}d", ret); + volume = reply.ReadFloat(); + + return ret; +} + +int32_t IpcStreamProxy::SetAudioEffectMode(int32_t effectMode) +{ + MessageParcel data; + MessageParcel reply; + MessageOption option; + + CHECK_AND_RETURN_RET_LOG(data.WriteInterfaceToken(GetDescriptor()), ERROR, "Write descriptor failed!"); + + data.WriteInt32(effectMode); + int ret = Remote()->SendRequest(IpcStreamMsg::ON_SET_EFFECT_MODE, data, reply, option); + CHECK_AND_RETURN_RET_LOG(ret == AUDIO_OK, ERR_OPERATION_FAILED, "SetAudioEffectMode failed, ipc error: %{public}d", + ret); + + return reply.ReadInt32(); +} + +int32_t IpcStreamProxy::GetAudioEffectMode(int32_t &effectMode) +{ + MessageParcel data; + MessageParcel reply; + MessageOption option; + + CHECK_AND_RETURN_RET_LOG(data.WriteInterfaceToken(GetDescriptor()), ERROR, "Write descriptor failed!"); + + int ret = Remote()->SendRequest(IpcStreamMsg::ON_GET_EFFECT_MODE, data, reply, option); + CHECK_AND_RETURN_RET_LOG(ret == AUDIO_OK, ret, "GetAudioEffectMode failed, ipc error: %{public}d", ret); + ret = reply.ReadInt32(); + CHECK_AND_RETURN_RET_LOG(ret == SUCCESS, ret, "GetAudioEffectMode failed, error: %{public}d", ret); + effectMode = reply.ReadInt32(); + + return ret; +} + +int32_t IpcStreamProxy::SetPrivacyType(int32_t privacyType) +{ + MessageParcel data; + MessageParcel reply; + MessageOption option; + + CHECK_AND_RETURN_RET_LOG(data.WriteInterfaceToken(GetDescriptor()), ERROR, "Write descriptor failed!"); + + int ret = Remote()->SendRequest(IpcStreamMsg::ON_SET_PRIVACY_TYPE, data, reply, option); + CHECK_AND_RETURN_RET_LOG(ret == AUDIO_OK, ERR_OPERATION_FAILED, "SetPrivacyType failed, error: %{public}d", ret); + + return reply.ReadInt32(); +} + +int32_t IpcStreamProxy::GetPrivacyType(int32_t &privacyType) +{ + MessageParcel data; + MessageParcel reply; + MessageOption option; + + CHECK_AND_RETURN_RET_LOG(data.WriteInterfaceToken(GetDescriptor()), ERROR, "Write descriptor failed!"); + + int ret = Remote()->SendRequest(IpcStreamMsg::ON_GET_PRIVACY_TYPE, data, reply, option); + CHECK_AND_RETURN_RET_LOG(ret == AUDIO_OK, ret, "GetPrivacyType failed, ipc error: %{public}d", ret); + ret = reply.ReadInt32(); + CHECK_AND_RETURN_RET_LOG(ret == SUCCESS, ret, "GetPrivacyType failed, error: %{public}d", ret); + privacyType = reply.ReadInt32(); + + return ret; +} +} // namespace AudioStandard +} // namespace OHOS diff --git a/services/audio_service/client/src/renderer_in_client.cpp b/services/audio_service/client/src/renderer_in_client.cpp new file mode 100644 index 0000000000000000000000000000000000000000..25bb2cc495d6a34e7aa7ab516f27e33d834d2f58 --- /dev/null +++ b/services/audio_service/client/src/renderer_in_client.cpp @@ -0,0 +1,1738 @@ +/* + * 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 "renderer_in_client.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "iservice_registry.h" +#include "system_ability_definition.h" +#include "securec.h" +#include "safe_block_queue.h" + +#include "audio_errors.h" +#include "audio_manager_base.h" +#include "audio_log.h" +#include "audio_ring_cache.h" +#include "audio_channel_blend.h" +#include "audio_server_death_recipient.h" +#include "audio_stream_tracker.h" +#include "audio_system_manager.h" +#include "audio_utils.h" +#include "ipc_stream_listener_impl.h" +#include "ipc_stream_listener_stub.h" +#include "volume_ramp.h" +#include "callback_handler.h" + +namespace OHOS { +namespace AudioStandard { +namespace { +const uint64_t OLD_BUF_DURATION_IN_USEC = 92880; // This value is used for compatibility purposes. +const uint64_t AUDIO_US_PER_MS = 1000; +const uint64_t AUDIO_US_PER_S = 1000000; +const uint64_t MAX_BUF_DURATION_IN_USEC = 2000000; // 2S +static const size_t MAX_WRITE_SIZE = 20 * 1024 * 1024; // 20M +static const int32_t CREATE_TIMEOUT_IN_SECOND = 5; // 5S +static const int32_t OPERATION_TIMEOUT_IN_MS = 500; // 500ms +static const int32_t SHORT_TIMEOUT_IN_MS = 20; // ms +static constexpr int CB_QUEUE_CAPACITY = 1; +} +class RendererInClientInner : public RendererInClient, public IStreamListener, public IHandler, + public std::enable_shared_from_this { +public: + RendererInClientInner(AudioStreamType eStreamType, int32_t appUid); + ~RendererInClientInner(); + + // IStreamListener + int32_t OnOperationHandled(Operation operation, int64_t result) override; + + // IAudioStream + 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 CheckRecordingCreate(uint32_t appTokenId, uint64_t appFullTokenId, int32_t appUid, SourceType sourceType = + SOURCE_TYPE_MIC) override; + bool CheckRecordingStateChange(uint32_t appTokenId, uint64_t appFullTokenId, int32_t appUid, + 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; + int32_t SetSpeed(float speed) override; + float GetSpeed() override; + int32_t ChangeSpeed(uint8_t *buffer, int32_t bufferSize, std::unique_ptr &outBuffer, + int32_t &outBufferSize) override; + int32_t WriteSpeedBuffer(int32_t bufferSize, uint8_t *speedBuffer, size_t speedBufferSize) 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; + int32_t SetOffloadMode(int32_t state, bool isAppBack) override; + int32_t UnsetOffloadMode() 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 SetWakeupCapturerState(bool isWakeupCapturer) override; + void SetCapturerSource(int capturerSource) 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; + int32_t Write(uint8_t *buffer, size_t bufferSize) override; + int32_t Write(uint8_t *pcmBuffer, size_t pcmSize, uint8_t *metaBuffer, size_t metaSize) override; + void SetPreferredFrameSize(int32_t frameSize) 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; + int32_t SetChannelBlendMode(ChannelBlendMode blendMode) override; + int32_t SetVolumeWithRamp(float volume, int32_t duration) override; + + void SetStreamTrackerState(bool trackerRegisteredState) override; + void GetSwitchInfo(IAudioStream::SwitchInfo& info) override; + + IAudioStream::StreamClass GetStreamClass() override; + + static const sptr GetAudioServerProxy(); + static void AudioServerDied(pid_t pid); + + void OnHandle(uint32_t code, int64_t data) override; + void InitCallbackHandler(); + + int32_t StateCmdTypeToParams(int64_t ¶ms, State state, StateChangeCmdType cmdType); + int32_t ParamsToStateCmdType(int64_t params, State &state, StateChangeCmdType &cmdType); + + void SendRenderMarkReachedEvent(int64_t rendererMarkPosition); + void SendRenderPeriodReachedEvent(int64_t rendererPeriodSize); + + void HandleRendererPositionChanges(size_t bytesWritten); + void HandleStateChangeEvent(int64_t data); + void HandleRenderMarkReachedEvent(int64_t data); + void HandleRenderPeriodReachedEvent(int64_t data); + +private: + void RegisterTracker(const std::shared_ptr &proxyObj); + void UpdateTracker(const std::string &updateCase); + + int32_t DeinitIpcStream(); + + int32_t InitIpcStream(); + + const AudioProcessConfig ConstructConfig(); + + int32_t InitSharedBuffer(); + int32_t InitCacheBuffer(size_t targetSize); + + int32_t FlushRingCache(); + int32_t DrainRingCache(); + + int32_t WriteCacheData(); + + void InitCallbackBuffer(uint64_t bufferDurationInUs); + void WriteCallbackFunc(); + // for callback mode. Check status if not running, wait for start or release. + bool WaitForRunning(); +private: + AudioStreamType eStreamType_; + int32_t appUid_; + uint32_t sessionId_; + int32_t clientPid_ = -1; + int32_t clientUid_ = -1; + + std::unique_ptr audioStreamTracker_; + + AudioRendererInfo rendererInfo_; + AudioCapturerInfo capturerInfo_; // not in use + + AudioPrivacyType privacyType_ = PRIVACY_TYPE_PUBLIC; + bool streamTrackerRegistered_ = false; + + bool needSetThreadPriority_ = true; + + AudioStreamParams streamParams_; // in plan next: replace it with AudioRendererParams + + // for data process + bool isBlendSet_ = false; + AudioBlend audioBlend_; + VolumeRamp volumeRamp_; + + // callbacks + std::mutex streamCbMutex_; + std::weak_ptr streamCallback_; + + size_t cacheSizeInByte_ = 0; + uint32_t spanSizeInFrame_ = 0; + size_t clientSpanSizeInByte_ = 0; + size_t sizePerFrameInByte_ = 4; // 16bit 2ch as default + + int32_t bufferSizeInMsec_ = 20; // 20ms + std::string cachePath_; + + // callback mode releated + AudioRenderMode renderMode_ = RENDER_MODE_NORMAL; + std::thread callbackLoop_; // thread for callback to client and write. + std::atomic cbThreadReleased_ = true; + std::mutex writeCbMutex_; + std::condition_variable cbThreadCv_; + std::shared_ptr writeCb_ = nullptr; + std::mutex cbBufferMutex_; + std::unique_ptr cbBuffer_ {nullptr}; + size_t cbBufferSize_ = 0; + SafeBlockQueue cbBufferQueue_; // only one cbBuffer_ + + std::atomic state_ = INVALID; + // using this lock when change status_ + std::mutex statusMutex_; + // for status operation wait and notify + std::mutex callServerMutex_; + std::condition_variable callServerCV_; + + Operation notifiedOperation_ = MAX_OPERATION_CODE; + int64_t notifiedResult_ = 0; + + // write data + std::mutex writeDataMutex_; + std::condition_variable writeDataCV_; + + float clientVolume_ = 1.0; + + uint64_t clientWrittenBytes_ = 0; + uint32_t underrunCount_ = 0; + // ipc stream related + AudioProcessConfig clientConfig_; + sptr listener_ = nullptr; + sptr ipcStream_ = nullptr; + std::shared_ptr clientBuffer_ = nullptr; + + // buffer handle + std::unique_ptr ringCache_ = nullptr; + std::mutex writeMutex_; // used for prevent multi thread call write + + // Mark reach and period reach callback + int64_t totalBytesWritten_ = 0; + std::mutex markReachMutex_; + bool rendererMarkReached_ = false; + int64_t rendererMarkPosition_ = 0; + std::shared_ptr rendererPositionCallback_ = nullptr; + + std::mutex periodReachMutex_; + int64_t rendererPeriodSize_ = 0; + int64_t rendererPeriodWritten_ = 0; + std::shared_ptr rendererPeriodPositionCallback_ = nullptr; + + // Event handler + bool runnerReleased_ = false; + std::mutex runnerMutex_; + std::shared_ptr callbackHandler_ = nullptr; + + bool paramsIsSet_ = false; + AudioRendererRate rendererRate_ = RENDER_RATE_NORMAL; + + enum { + STATE_CHANGE_EVENT = 0, + RENDERER_MARK_REACHED_EVENT, + RENDERER_PERIOD_REACHED_EVENT, + CAPTURER_PERIOD_REACHED_EVENT, + CAPTURER_MARK_REACHED_EVENT, + }; + + // note that the starting elements should remain the same as the enum State + enum : int64_t { + HANDLER_PARAM_INVALID = -1, + HANDLER_PARAM_NEW = 0, + HANDLER_PARAM_PREPARED, + HANDLER_PARAM_RUNNING, + HANDLER_PARAM_STOPPED, + HANDLER_PARAM_RELEASED, + HANDLER_PARAM_PAUSED, + HANDLER_PARAM_STOPPING, + HANDLER_PARAM_RUNNING_FROM_SYSTEM, + HANDLER_PARAM_PAUSED_FROM_SYSTEM, + }; +}; + +std::shared_ptr RendererInClient::GetInstance(AudioStreamType eStreamType, int32_t appUid) +{ + return std::make_shared(eStreamType, appUid); +} + +RendererInClientInner::RendererInClientInner(AudioStreamType eStreamType, int32_t appUid) + : eStreamType_(eStreamType), appUid_(appUid), cbBufferQueue_(CB_QUEUE_CAPACITY) +{ + AUDIO_INFO_LOG("Create with StreamType:%{public}d appUid:%{public}d ", eStreamType_, appUid_); + audioStreamTracker_ = std::make_unique(AUDIO_MODE_PLAYBACK, appUid); + state_ = NEW; +} + +RendererInClientInner::~RendererInClientInner() +{ + AUDIO_INFO_LOG("~RendererInClientInner()"); +} + +int32_t RendererInClientInner::OnOperationHandled(Operation operation, int64_t result) +{ + // read/write operation may print many log, use debug. + if (operation == UPDATE_STREAM) { + AUDIO_DEBUG_LOG("OnOperationHandled() UPDATE_STREAM result:%{public}" PRId64".", result); + // notify write if blocked + writeDataCV_.notify_all(); + return SUCCESS; + } + if (operation == BUFFER_UNDERRUN) { + underrunCount_++; + AUDIO_WARNING_LOG("recv underrun %{public}d", underrunCount_); + // in plan next: do more to reduce underrun + writeDataCV_.notify_all(); + return SUCCESS; + } + AUDIO_INFO_LOG("OnOperationHandled() recv operation:%{public}d result:%{public}" PRId64".", operation, result); + std::unique_lock lock(callServerMutex_); + notifiedOperation_ = operation; + notifiedResult_ = result; + callServerCV_.notify_all(); + return SUCCESS; +} + +void RendererInClientInner::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 RendererInClientInner::SetRendererInfo(const AudioRendererInfo &rendererInfo) +{ + rendererInfo_ = rendererInfo; + AUDIO_INFO_LOG("SetRendererInfo with flag %{public}d", rendererInfo_.rendererFlags); +} + +void RendererInClientInner::SetCapturerInfo(const AudioCapturerInfo &capturerInfo) +{ + AUDIO_WARNING_LOG("SetCapturerInfo is not supported"); + return; +} + +void RendererInClientInner::RegisterTracker(const std::shared_ptr &proxyObj) +{ + if (audioStreamTracker_ && audioStreamTracker_.get() && !streamTrackerRegistered_) { + // make sure sessionId_ is valid. + AUDIO_INFO_LOG("Calling register tracker, sessionid is %{public}d", sessionId_); + AudioRegisterTrackerInfo registerTrackerInfo; + + registerTrackerInfo.sessionId = sessionId_; + registerTrackerInfo.clientPid = clientPid_; + registerTrackerInfo.state = state_; + registerTrackerInfo.rendererInfo = rendererInfo_; + registerTrackerInfo.capturerInfo = capturerInfo_; + + audioStreamTracker_->RegisterTracker(registerTrackerInfo, proxyObj); + streamTrackerRegistered_ = true; + } +} + +void RendererInClientInner::UpdateTracker(const std::string &updateCase) +{ + if (audioStreamTracker_ && audioStreamTracker_.get()) { + AUDIO_DEBUG_LOG("Renderer:Calling Update tracker for %{public}s", updateCase.c_str()); + audioStreamTracker_->UpdateTracker(sessionId_, state_, clientPid_, rendererInfo_, capturerInfo_); + } +} + +int32_t RendererInClientInner::SetAudioStreamInfo(const AudioStreamParams info, + const std::shared_ptr &proxyObj) +{ + // In plan: If paramsIsSet_ is true, and new info is same as old info, return + AUDIO_INFO_LOG("AudioStreamInfo, Sampling rate: %{public}d, channels: %{public}d, format: %{public}d," + " stream type: %{public}d, encoding type: %{public}d", info.samplingRate, info.channels, info.format, + eStreamType_, info.encoding); + + AudioXCollie guard("RendererInClientInner::SetAudioStreamInfo", CREATE_TIMEOUT_IN_SECOND); + if (!IsFormatValid(info.format) || !IsSamplingRateValid(info.samplingRate) || !IsEncodingTypeValid(info.encoding)) { + AUDIO_ERR_LOG("Unsupported audio parameter"); + return ERR_NOT_SUPPORTED; + } + if (!IsPlaybackChannelRelatedInfoValid(info.channels, info.channelLayout)) { + return ERR_NOT_SUPPORTED; + } + + CHECK_AND_RETURN_RET_LOG(IAudioStream::GetByteSizePerFrame(info, sizePerFrameInByte_) == SUCCESS, + ERROR_INVALID_PARAM, "GetByteSizePerFrame failed with invalid params"); + + if (state_ != NEW) { + AUDIO_ERR_LOG("State is not new, release existing stream and recreate, state %{public}d", state_.load()); + int32_t ret = DeinitIpcStream(); + CHECK_AND_RETURN_RET_LOG(ret == SUCCESS, ret, "release existing stream failed."); + } + + streamParams_ = info; // keep it for later use + paramsIsSet_ = true; + int32_t initRet = InitIpcStream(); + CHECK_AND_RETURN_RET_LOG(initRet == SUCCESS, initRet, "Init stream failed: %{public}d", initRet); + state_ = PREPARED; + + RegisterTracker(proxyObj); + return SUCCESS; +} + +std::mutex g_serverProxyMutex; +sptr gServerProxy_ = nullptr; +const sptr RendererInClientInner::GetAudioServerProxy() +{ + std::lock_guard lock(g_serverProxyMutex); + if (gServerProxy_ == nullptr) { + auto samgr = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager(); + if (samgr == nullptr) { + AUDIO_ERR_LOG("GetAudioServerProxy: get sa manager failed"); + return nullptr; + } + sptr object = samgr->GetSystemAbility(AUDIO_DISTRIBUTED_SERVICE_ID); + if (object == nullptr) { + AUDIO_ERR_LOG("GetAudioServerProxy: get audio service remote object failed"); + return nullptr; + } + gServerProxy_ = iface_cast(object); + if (gServerProxy_ == nullptr) { + AUDIO_ERR_LOG("GetAudioServerProxy: get audio service proxy failed"); + return nullptr; + } + + // register death recipent to restore proxy + sptr asDeathRecipient = new(std::nothrow) AudioServerDeathRecipient(getpid()); + if (asDeathRecipient != nullptr) { + asDeathRecipient->SetNotifyCb(std::bind(&RendererInClientInner::AudioServerDied, + std::placeholders::_1)); + bool result = object->AddDeathRecipient(asDeathRecipient); + if (!result) { + AUDIO_ERR_LOG("GetAudioServerProxy: failed to add deathRecipient"); + } + } + } + sptr gasp = gServerProxy_; + return gasp; +} + +void RendererInClientInner::AudioServerDied(pid_t pid) +{ + AUDIO_INFO_LOG("audio server died clear proxy, will restore proxy in next call"); + std::lock_guard lock(g_serverProxyMutex); + gServerProxy_ = nullptr; +} + +void RendererInClientInner::OnHandle(uint32_t code, int64_t data) +{ + AUDIO_DEBUG_LOG("On handle event, event code: %{public}d, data: %{public}" PRIu64 "", code, data); + switch (code) { + case STATE_CHANGE_EVENT: + HandleStateChangeEvent(data); + break; + case RENDERER_MARK_REACHED_EVENT: + HandleRenderMarkReachedEvent(data); + break; + case RENDERER_PERIOD_REACHED_EVENT: + HandleRenderPeriodReachedEvent(data); + break; + default: + break; + } +} + +void RendererInClientInner::HandleStateChangeEvent(int64_t data) +{ + State state = INVALID; + StateChangeCmdType cmdType = CMD_FROM_CLIENT; + ParamsToStateCmdType(data, state, cmdType); + std::unique_lock lock(streamCbMutex_); + std::shared_ptr streamCb = streamCallback_.lock(); + if (streamCb != nullptr) { + state = state != STOPPING ? state : STOPPED; // client only need STOPPED + streamCb->OnStateChange(state, cmdType); + } +} + +void RendererInClientInner::HandleRenderMarkReachedEvent(int64_t rendererMarkPosition) +{ + AUDIO_DEBUG_LOG("Start HandleRenderMarkReachedEvent"); + std::unique_lock lock(markReachMutex_); + if (rendererPositionCallback_) { + rendererPositionCallback_->OnMarkReached(rendererMarkPosition); + } +} + +void RendererInClientInner::HandleRenderPeriodReachedEvent(int64_t rendererPeriodNumber) +{ + AUDIO_DEBUG_LOG("Start HandleRenderPeriodReachedEvent"); + std::unique_lock lock(periodReachMutex_); + if (rendererPeriodPositionCallback_) { + rendererPeriodPositionCallback_->OnPeriodReached(rendererPeriodNumber); + } +} + +void RendererInClientInner::InitCallbackHandler() +{ + if (callbackHandler_ == nullptr) { + callbackHandler_ = CallbackHandler::GetInstance(shared_from_this()); + } +} + +// call this without lock, we should be able to call deinit in any case. +int32_t RendererInClientInner::DeinitIpcStream() +{ + ipcStream_->Release(); + // in plan: + ipcStream_ = nullptr; + ringCache_->ResetBuffer(); + return SUCCESS; +} + +const AudioProcessConfig RendererInClientInner::ConstructConfig() +{ + AudioProcessConfig config = {}; + // in plan: get token id + config.appInfo.appPid = clientPid_; + config.appInfo.appUid = clientUid_; + + config.streamInfo.channels = static_cast(streamParams_.channels); + config.streamInfo.encoding = static_cast(streamParams_.encoding); + config.streamInfo.format = static_cast(streamParams_.format); + config.streamInfo.samplingRate = static_cast(streamParams_.samplingRate); + + config.audioMode = AUDIO_MODE_PLAYBACK; + + config.rendererInfo = rendererInfo_; + if (rendererInfo_.rendererFlags != 0) { + AUDIO_WARNING_LOG("ConstructConfig find renderer flag invalid:%{public}d", rendererInfo_.rendererFlags); + rendererInfo_.rendererFlags = 0; + } + + config.capturerInfo = {}; + + config.streamType = eStreamType_; + + // in plan: add privacyType_ + + clientConfig_ = config; + + return config; +} + +int32_t RendererInClientInner::InitSharedBuffer() +{ + CHECK_AND_RETURN_RET_LOG(ipcStream_ != nullptr, ERR_OPERATION_FAILED, "InitSharedBuffer failed, null ipcStream_."); + int32_t ret = ipcStream_->ResolveBuffer(clientBuffer_); + + CHECK_AND_RETURN_RET_LOG(ret == SUCCESS && clientBuffer_ != nullptr, ret, "ResolveBuffer failed:%{public}d", ret); + + uint32_t totalSizeInFrame = 0; + uint32_t byteSizePerFrame = 0; + ret = clientBuffer_->GetSizeParameter(totalSizeInFrame, spanSizeInFrame_, byteSizePerFrame); + + CHECK_AND_RETURN_RET_LOG(ret == SUCCESS && byteSizePerFrame == sizePerFrameInByte_, ret, "GetSizeParameter failed" + ":%{public}d, byteSizePerFrame:%{public}u, sizePerFrameInByte_:%{public}zu", ret, byteSizePerFrame, + sizePerFrameInByte_); + + clientSpanSizeInByte_ = spanSizeInFrame_ * byteSizePerFrame; + + AUDIO_INFO_LOG("totalSizeInFrame_[%{public}u] spanSizeInFrame[%{public}u] sizePerFrameInByte_[%{public}zu]" + "clientSpanSizeInByte_[%{public}zu]", totalSizeInFrame, spanSizeInFrame_, sizePerFrameInByte_, + clientSpanSizeInByte_); + + return SUCCESS; +} + +// InitCacheBuffer should be able to modify the cache size between clientSpanSizeInByte_ and 4 * clientSpanSizeInByte_ +int32_t RendererInClientInner::InitCacheBuffer(size_t targetSize) +{ + CHECK_AND_RETURN_RET_LOG(clientSpanSizeInByte_ != 0, ERR_OPERATION_FAILED, "clientSpanSizeInByte_ invalid"); + + AUDIO_INFO_LOG("old size:%{public}zu, new size:%{public}zu", cacheSizeInByte_, targetSize); + cacheSizeInByte_ = targetSize; + + if (ringCache_ == nullptr) { + ringCache_ = AudioRingCache::Create(cacheSizeInByte_); + } else { + OptResult result = ringCache_->ReConfig(cacheSizeInByte_, false); // false --> clear buffer + if (result.ret != OPERATION_SUCCESS) { + AUDIO_ERR_LOG("ReConfig AudioRingCache to size %{public}u failed:ret%{public}zu", result.ret, targetSize); + return ERR_OPERATION_FAILED; + } + } + + return SUCCESS; +} + +int32_t RendererInClientInner::InitIpcStream() +{ + AudioProcessConfig config = ConstructConfig(); + sptr gasp = RendererInClientInner::GetAudioServerProxy(); + CHECK_AND_RETURN_RET_LOG(gasp != nullptr, ERR_OPERATION_FAILED, "Create failed, can not get service."); + sptr ipcProxy = gasp->CreateAudioProcess(config); // in plan next: add ret + CHECK_AND_RETURN_RET_LOG(ipcProxy != nullptr, ERR_OPERATION_FAILED, "failed with null ipcProxy."); + ipcStream_ = iface_cast(ipcProxy); + CHECK_AND_RETURN_RET_LOG(ipcStream_ != nullptr, ERR_OPERATION_FAILED, "failed when iface_cast."); + + // in plan next: old listener_ is destoried here, will server receive dieth notify? + listener_ = sptr::MakeSptr(shared_from_this()); + int32_t ret = ipcStream_->RegisterStreamListener(listener_->AsObject()); + CHECK_AND_RETURN_RET_LOG(ret == SUCCESS, ret, "RegisterStreamListener failed:%{public}d", ret); + + ret = InitSharedBuffer(); + CHECK_AND_RETURN_RET_LOG(ret == SUCCESS, ret, "InitSharedBuffer failed:%{public}d", ret); + + ret = InitCacheBuffer(clientSpanSizeInByte_); + CHECK_AND_RETURN_RET_LOG(ret == SUCCESS, ret, "InitCacheBuffer failed:%{public}d", ret); + + ret = ipcStream_->GetAudioSessionID(sessionId_); + CHECK_AND_RETURN_RET_LOG(ret == SUCCESS, ret, "GetAudioSessionID failed:%{public}d", ret); + InitCallbackHandler(); + return SUCCESS; +} + +int32_t RendererInClientInner::GetAudioStreamInfo(AudioStreamParams &info) +{ + CHECK_AND_RETURN_RET_LOG(paramsIsSet_ == true, ERR_OPERATION_FAILED, "Params is not set"); + info = streamParams_; + return SUCCESS; +} + +bool RendererInClientInner::CheckRecordingCreate(uint32_t appTokenId, uint64_t appFullTokenId, int32_t appUid, + SourceType sourceType) +{ + AUDIO_WARNING_LOG("CheckRecordingCreate is not supported"); + return false; +} + +bool RendererInClientInner::CheckRecordingStateChange(uint32_t appTokenId, uint64_t appFullTokenId, int32_t appUid, + AudioPermissionState state) +{ + AUDIO_WARNING_LOG("CheckRecordingCreate is not supported"); + return false; +} + +int32_t RendererInClientInner::GetAudioSessionID(uint32_t &sessionID) +{ + CHECK_AND_RETURN_RET_LOG((state_ != RELEASED) && (state_ != NEW), ERR_ILLEGAL_STATE, + "State error %{public}d", state_.load()); + sessionID = sessionId_; + return SUCCESS; +} + +State RendererInClientInner::GetState() +{ + return state_; +} + +bool RendererInClientInner::GetAudioTime(Timestamp ×tamp, Timestamp::Timestampbase base) +{ + CHECK_AND_RETURN_RET_LOG(paramsIsSet_ == true, ERR_OPERATION_FAILED, "Params is not set"); + // in plan: + return false; +} + +int32_t RendererInClientInner::GetBufferSize(size_t &bufferSize) +{ + CHECK_AND_RETURN_RET_LOG(state_ != RELEASED, ERR_ILLEGAL_STATE, "Capturer stream is released"); + bufferSize = clientSpanSizeInByte_; + if (renderMode_ == RENDER_MODE_CALLBACK) { + bufferSize = cbBufferSize_; + } + AUDIO_INFO_LOG("Buffer size is %{public}zu, mode is %{public}s", bufferSize, renderMode_ == RENDER_MODE_NORMAL ? + "RENDER_MODE_NORMAL" : "RENDER_MODE_CALLBACK"); + return SUCCESS; +} + +int32_t RendererInClientInner::GetFrameCount(uint32_t &frameCount) +{ + CHECK_AND_RETURN_RET_LOG(state_ != RELEASED, ERR_ILLEGAL_STATE, "Capturer stream is released"); + CHECK_AND_RETURN_RET_LOG(sizePerFrameInByte_ != 0, ERR_ILLEGAL_STATE, "sizePerFrameInByte_ is 0!"); + frameCount = spanSizeInFrame_; + if (renderMode_ == RENDER_MODE_CALLBACK) { + frameCount = cbBufferSize_ / sizePerFrameInByte_; + } + AUDIO_INFO_LOG("Frame count is %{public}u, mode is %{public}s", frameCount, renderMode_ == RENDER_MODE_NORMAL ? + "RENDER_MODE_NORMAL" : "RENDER_MODE_CALLBACK"); + return SUCCESS; +} + +int32_t RendererInClientInner::GetLatency(uint64_t &latency) +{ + // in plan: 150000 for debug + latency = 150000; // unit is us, 150000 is 150ms + return SUCCESS; +} + +int32_t RendererInClientInner::SetAudioStreamType(AudioStreamType audioStreamType) +{ + AUDIO_ERR_LOG("Change stream type %{public}d to %{public}d is not supported", eStreamType_, audioStreamType); + return SUCCESS; +} + +int32_t RendererInClientInner::SetVolume(float volume) +{ + Trace trace("RendererInClientInner::SetVolume:" + std::to_string(volume)); + if (volume < 0.0 || volume > 1.0) { + AUDIO_ERR_LOG("SetVolume with invalid volume %{public}f", volume); + return ERR_INVALID_PARAM; + } + clientVolume_ = volume; // in plan: using volumetools in write or enqueue + return SUCCESS; +} + +float RendererInClientInner::GetVolume() +{ + Trace trace("RendererInClientInner::GetVolume:" + std::to_string(clientVolume_)); + return clientVolume_; +} + +int32_t RendererInClientInner::SetRenderRate(AudioRendererRate renderRate) +{ + CHECK_AND_RETURN_RET_LOG(ipcStream_ != nullptr, ERR_ILLEGAL_STATE, "ipcStream is not inited!"); + rendererRate_ = renderRate; + return ipcStream_->SetRate(renderRate); +} + +int32_t RendererInClientInner::SetSpeed(float speed) +{ + // in plan + return ERROR; +} + +float RendererInClientInner::GetSpeed() +{ + // in plan + return 1.0; +} + +int32_t RendererInClientInner::ChangeSpeed(uint8_t *buffer, int32_t bufferSize, std::unique_ptr &outBuffer, + int32_t &outBufferSize) +{ + // in plan + return ERROR; +} + +int32_t RendererInClientInner::WriteSpeedBuffer(int32_t bufferSize, uint8_t *speedBuffer, size_t speedBufferSize) +{ + // in plan + return ERROR; +} + +AudioRendererRate RendererInClientInner::GetRenderRate() +{ + AUDIO_INFO_LOG("Get RenderRate %{public}d", rendererRate_); + return rendererRate_; +} + +int32_t RendererInClientInner::SetStreamCallback(const std::shared_ptr &callback) +{ + if (callback == nullptr) { + AUDIO_ERR_LOG("SetStreamCallback failed. callback == nullptr"); + return ERR_INVALID_PARAM; + } + + std::unique_lock lock(streamCbMutex_); + streamCallback_ = callback; + lock.unlock(); + + if (state_ != PREPARED) { + return SUCCESS; + } + callbackHandler_->SendCallbackEvent(STATE_CHANGE_EVENT, PREPARED); + return SUCCESS; +} + +void RendererInClientInner::InitCallbackBuffer(uint64_t bufferDurationInUs) +{ + if (bufferDurationInUs > MAX_BUF_DURATION_IN_USEC) { + AUDIO_ERR_LOG("InitCallbackBuffer with invalid duration %{public}" PRIu64", use default instead.", + bufferDurationInUs); + bufferDurationInUs = OLD_BUF_DURATION_IN_USEC; + } + // Calculate buffer size based on duration. + + cbBufferSize_ = static_cast(bufferDurationInUs * streamParams_.samplingRate / AUDIO_US_PER_S) * + sizePerFrameInByte_; + AUDIO_INFO_LOG("InitCallbackBuffer with duration %{public}" PRIu64", size: %{public}zu", bufferDurationInUs, + cbBufferSize_); + std::lock_guard lock(cbBufferMutex_); + cbBuffer_ = std::make_unique(cbBufferSize_); +} + +int32_t RendererInClientInner::SetRenderMode(AudioRenderMode renderMode) +{ + AUDIO_INFO_LOG("SetRenderMode to %{public}s", renderMode == RENDER_MODE_NORMAL ? "RENDER_MODE_NORMAL" : + "RENDER_MODE_CALLBACK"); + if (renderMode_ == renderMode) { + return SUCCESS; + } + + // renderMode_ is inited as RENDER_MODE_NORMAL, can only be set to RENDER_MODE_CALLBACK. + if (renderMode_ == RENDER_MODE_CALLBACK && renderMode == RENDER_MODE_NORMAL) { + AUDIO_ERR_LOG("SetRenderMode from callback to normal is not supported."); + return ERR_INCORRECT_MODE; + } + + // state check + if (state_ != PREPARED && state_ != NEW) { + AUDIO_ERR_LOG("SetRenderMode failed. invalid state:%{public}d", state_.load()); + return ERR_ILLEGAL_STATE; + } + renderMode_ = renderMode; + + // init callbackLoop_ + callbackLoop_ = std::thread(&RendererInClientInner::WriteCallbackFunc, this); + pthread_setname_np(callbackLoop_.native_handle(), "OS_AudioWriteCB"); + + std::unique_lock threadStartlock(statusMutex_); + bool stopWaiting = cbThreadCv_.wait_for(threadStartlock, std::chrono::milliseconds(SHORT_TIMEOUT_IN_MS), [this] { + return cbThreadReleased_ == false; // When thread is started, cbThreadReleased_ will be false. So stop waiting. + }); + if (!stopWaiting) { + AUDIO_WARNING_LOG("Init OS_AudioWriteCB thread time out"); + } + + InitCallbackBuffer(OLD_BUF_DURATION_IN_USEC); + return SUCCESS; +} + +AudioRenderMode RendererInClientInner::GetRenderMode() +{ + AUDIO_INFO_LOG("Render mode is %{public}s", renderMode_ == RENDER_MODE_NORMAL ? "RENDER_MODE_NORMAL" : + "RENDER_MODE_CALLBACK"); + return renderMode_; +} + +int32_t RendererInClientInner::SetRendererWriteCallback(const std::shared_ptr &callback) +{ + CHECK_AND_RETURN_RET_LOG(callback != nullptr, ERR_INVALID_PARAM, "Invalid null callback"); + CHECK_AND_RETURN_RET_LOG(renderMode_ == RENDER_MODE_CALLBACK, ERR_INCORRECT_MODE, "incorrect render mode"); + std::lock_guard lock(writeCbMutex_); + writeCb_ = callback; + return SUCCESS; +} + +// Sleep or wait in WaitForRunning to avoid dead looping. +bool RendererInClientInner::WaitForRunning() +{ + Trace trace("RendererInClientInner::WaitForRunning"); + // check renderer state_: call client write only in running else wait on statusMutex_ + std::unique_lock stateLock(statusMutex_); + if (state_ != RUNNING) { + bool stopWaiting = cbThreadCv_.wait_for(stateLock, std::chrono::milliseconds(OPERATION_TIMEOUT_IN_MS), [this] { + return state_ == RUNNING || cbThreadReleased_; + }); + if (cbThreadReleased_) { + AUDIO_INFO_LOG("CBThread end in non-running status, sessionID :%{public}d", sessionId_); + return false; + } + if (!stopWaiting) { + AUDIO_INFO_LOG("Wait timeout, current state_ is %{public}d", state_.load()); // wait 0.5s + return false; + } + } + return true; +} + +void RendererInClientInner::WriteCallbackFunc() +{ + AUDIO_INFO_LOG("WriteCallbackFunc start, sessionID :%{public}d", sessionId_); + cbThreadReleased_ = false; + + // Modify thread priority is not need as first call write will do these work. + cbThreadCv_.notify_one(); + + // start loop + while (!cbThreadReleased_) { + Trace traceLoop("RendererInClientInner::WriteCallbackFunc"); + if (!WaitForRunning()) { + continue; + } + + // call client write + Trace traceCb("RendererInClientInner::OnWriteData"); + std::unique_lock lockCb(writeCbMutex_); + if (writeCb_ != nullptr) { + writeCb_->OnWriteData(cbBufferSize_); + } + lockCb.unlock(); + traceCb.End(); + + std::lock_guard lockBuffer(cbBufferMutex_); + // If client didn't call Enqueue in OnWriteData, pop will block here. + BufferDesc temp = cbBufferQueue_.Pop(); + if (temp.buffer == nullptr) { + AUDIO_WARNING_LOG("Queue pop error: get nullptr."); + break; + } + // call write here. + int32_t result = Write(temp.buffer, temp.bufLength); + if (result < 0 || result != cbBufferSize_) { + AUDIO_WARNING_LOG("Call write error, ret:%{public}d, cbBufferSize_:%{public}zu", result, cbBufferSize_); + } + } + AUDIO_INFO_LOG("CBThread end sessionID :%{public}d", sessionId_); +} + +int32_t RendererInClientInner::SetCaptureMode(AudioCaptureMode captureMode) +{ + AUDIO_ERR_LOG("SetCaptureMode is not supported"); + return ERROR; +} + +AudioCaptureMode RendererInClientInner::GetCaptureMode() +{ + AUDIO_ERR_LOG("GetCaptureMode is not supported"); + return CAPTURE_MODE_NORMAL; // not supported +} + +int32_t RendererInClientInner::SetCapturerReadCallback(const std::shared_ptr &callback) +{ + AUDIO_ERR_LOG("SetCapturerReadCallback is not supported"); + return ERROR; +} + +int32_t RendererInClientInner::GetBufferDesc(BufferDesc &bufDesc) +{ + Trace trace("RendererInClientInner::GetBufferDesc"); + if (renderMode_ != RENDER_MODE_CALLBACK) { + AUDIO_ERR_LOG("GetBufferDesc is not supported. Render mode is not callback."); + return ERR_INCORRECT_MODE; + } + std::lock_guard lock(cbBufferMutex_); + bufDesc.buffer = cbBuffer_.get(); + bufDesc.bufLength = cbBufferSize_; + bufDesc.dataLength = cbBufferSize_; + return SUCCESS; +} + +int32_t RendererInClientInner::GetBufQueueState(BufferQueueState &bufState) +{ + Trace trace("RendererInClientInner::GetBufQueueState"); + if (renderMode_ != RENDER_MODE_CALLBACK) { + AUDIO_ERR_LOG("GetBufQueueState is not supported. Render mode is not callback."); + return ERR_INCORRECT_MODE; + } + // only one buffer in queue. + bufState.numBuffers = 1; + bufState.currentIndex = 0; + return SUCCESS; +} + +int32_t RendererInClientInner::Enqueue(const BufferDesc &bufDesc) +{ + Trace trace("RendererInClientInner::Enqueue"); + if (renderMode_ != RENDER_MODE_CALLBACK) { + AUDIO_ERR_LOG("Enqueue is not supported. Render mode is not callback."); + return ERR_INCORRECT_MODE; + } + std::lock_guard lock(cbBufferMutex_); + + if (bufDesc.bufLength != cbBufferSize_ || bufDesc.dataLength != cbBufferSize_) { + AUDIO_ERR_LOG("Enqueue invalid bufLength:%{public}zu or dataLength:%{public}zu, should be %{public}zu", + bufDesc.bufLength, bufDesc.dataLength, cbBufferSize_); + return ERR_INVALID_INDEX; + } + if (bufDesc.buffer != cbBuffer_.get()) { + AUDIO_WARNING_LOG("Enqueue buffer is not from us. Let's copy."); + int ret = memcpy_s(static_cast(cbBuffer_.get()), cbBufferSize_, static_cast(bufDesc.buffer), + bufDesc.bufLength); + CHECK_AND_RETURN_RET_LOG(ret == EOK, ERR_READ_BUFFER, "copy failed, ret is:%{public}d", ret); + } + BufferDesc temp = {cbBuffer_.get(), cbBufferSize_, cbBufferSize_}; + cbBufferQueue_.Push(temp); + // Call write may block, so put it in loop callbackLoop_ + return SUCCESS; +} + +int32_t RendererInClientInner::Clear() +{ + Trace trace("RendererInClientInner::Clear"); + if (renderMode_ != RENDER_MODE_CALLBACK) { + AUDIO_ERR_LOG("Clear is not supported. Render mode is not callback."); + return ERR_INCORRECT_MODE; + } + std::lock_guard lock(cbBufferMutex_); + int32_t ret = memset_s(cbBuffer_.get(), cbBufferSize_, 0, cbBufferSize_); + CHECK_AND_RETURN_RET_LOG(ret == EOK, ERR_OPERATION_FAILED, "Clear buffer fail, ret %{public}d.", ret); + return SUCCESS; +} + + +int32_t RendererInClientInner::SetLowPowerVolume(float volume) +{ + // in plan + return ERROR; +} + +float RendererInClientInner::GetLowPowerVolume() +{ + // in plan + return 0.0; +} + +int32_t RendererInClientInner::SetOffloadMode(int32_t state, bool isAppBack) +{ + // in plan + return ERROR; +} + +int32_t RendererInClientInner::UnsetOffloadMode() +{ + // in plan + return ERROR; +} + +float RendererInClientInner::GetSingleStreamVolume() +{ + // in plan + return 0.0; +} + +AudioEffectMode RendererInClientInner::GetAudioEffectMode() +{ + // in plan + return EFFECT_DEFAULT; +} + +int32_t RendererInClientInner::SetAudioEffectMode(AudioEffectMode effectMode) +{ + // in plan + return SUCCESS; +} + +int64_t RendererInClientInner::GetFramesWritten() +{ + return totalBytesWritten_; +} + +int64_t RendererInClientInner::GetFramesRead() +{ + AUDIO_ERR_LOG("not supported"); + return -1; +} + + +void RendererInClientInner::SetInnerCapturerState(bool isInnerCapturer) +{ + AUDIO_ERR_LOG("SetInnerCapturerState is not supported"); + return; +} + +void RendererInClientInner::SetWakeupCapturerState(bool isWakeupCapturer) +{ + AUDIO_ERR_LOG("SetWakeupCapturerState is not supported"); + return; +} + +void RendererInClientInner::SetCapturerSource(int capturerSource) +{ + AUDIO_ERR_LOG("SetCapturerSource is not supported"); + return; +} + +void RendererInClientInner::SetPrivacyType(AudioPrivacyType privacyType) +{ + privacyType_ = privacyType; + //in plan: should we update it after create? +} + +bool RendererInClientInner::StartAudioStream(StateChangeCmdType cmdType) +{ + Trace trace("RendererInClientInner::StartAudioStream " + std::to_string(sessionId_)); + std::unique_lock statusLock(statusMutex_); + if (state_ != PREPARED && state_ != STOPPED && state_ != PAUSED) { + AUDIO_ERR_LOG("Start failed Illegal state:%{public}d", state_.load()); + return false; + } + + CHECK_AND_RETURN_RET_LOG(ipcStream_ != nullptr, false, "ipcStream is not inited!"); + int32_t ret = ipcStream_->Start(); + if (ret != SUCCESS) { + AUDIO_ERR_LOG("Start call server failed:%{public}u", ret); + return false; + } + std::unique_lock waitLock(callServerMutex_); + bool stopWaiting = callServerCV_.wait_for(waitLock, std::chrono::milliseconds(OPERATION_TIMEOUT_IN_MS), [this] { + return notifiedOperation_ == START_STREAM; // will be false when got notified. + }); + + if (notifiedOperation_ != START_STREAM || notifiedResult_ != SUCCESS) { + AUDIO_ERR_LOG("Start failed: %{public}s Operation:%{public}d result:%{public}" PRId64".", + (!stopWaiting ? "timeout" : "no timeout"), notifiedOperation_, notifiedResult_); + return false; + } + waitLock.unlock(); + + state_ = RUNNING; // change state_ to RUNNING, then notify cbThread + if (renderMode_ == RENDER_MODE_CALLBACK) { + // start the callback-write thread + cbThreadCv_.notify_all(); + } + statusLock.unlock(); + // in plan: call HiSysEventWrite + int64_t param = -1; + StateCmdTypeToParams(param, state_, cmdType); + callbackHandler_->SendCallbackEvent(STATE_CHANGE_EVENT, param); + + AUDIO_INFO_LOG("Start SUCCESS, sessionId: %{public}d, uid: %{public}d", sessionId_, clientUid_); + UpdateTracker("RUNNING"); + return true; +} + +bool RendererInClientInner::PauseAudioStream(StateChangeCmdType cmdType) +{ + Trace trace("RendererInClientInner::PauseAudioStream " + std::to_string(sessionId_)); + std::unique_lock statusLock(statusMutex_); + if (state_ != RUNNING) { + AUDIO_ERR_LOG("State is not RUNNING. Illegal state:%{public}u", state_.load()); + return false; + } + + CHECK_AND_RETURN_RET_LOG(ipcStream_ != nullptr, false, "ipcStream is not inited!"); + int32_t ret = ipcStream_->Pause(); + if (ret != SUCCESS) { + AUDIO_ERR_LOG("call server failed:%{public}u", ret); + return false; + } + std::unique_lock waitLock(callServerMutex_); + bool stopWaiting = callServerCV_.wait_for(waitLock, std::chrono::milliseconds(OPERATION_TIMEOUT_IN_MS), [this] { + return notifiedOperation_ == PAUSE_STREAM; // will be false when got notified. + }); + + if (notifiedOperation_ != PAUSE_STREAM || notifiedResult_ != SUCCESS) { + AUDIO_ERR_LOG("Pause failed: %{public}s Operation:%{public}d result:%{public}" PRId64".", + (!stopWaiting ? "timeout" : "no timeout"), notifiedOperation_, notifiedResult_); + return false; + } + waitLock.unlock(); + + state_ = PAUSED; + statusLock.unlock(); + + // in plan: call HiSysEventWrite + int64_t param = -1; + StateCmdTypeToParams(param, state_, cmdType); + callbackHandler_->SendCallbackEvent(STATE_CHANGE_EVENT, param); + + AUDIO_INFO_LOG("Pause SUCCESS, sessionId %{public}d, uid %{public}d, mode %{public}s", sessionId_, + clientUid_, renderMode_ == RENDER_MODE_NORMAL ? "RENDER_MODE_NORMAL" : "RENDER_MODE_CALLBACK"); + UpdateTracker("PAUSED"); + return true; +} + +bool RendererInClientInner::StopAudioStream() +{ + Trace trace("RendererInClientInner::StopAudioStream " + std::to_string(sessionId_)); + AUDIO_INFO_LOG("Stop begin for sessionId %{public}d uid: %{public}d", sessionId_, clientUid_); + DrainAudioStream(); + std::unique_lock statusLock(statusMutex_); + if ((state_ != RUNNING) && (state_ != PAUSED)) { + AUDIO_ERR_LOG("Stop failed. Illegal state:%{public}u", state_.load()); + return false; + } + + CHECK_AND_RETURN_RET_LOG(ipcStream_ != nullptr, false, "ipcStream is not inited!"); + int32_t ret = ipcStream_->Stop(); + if (ret != SUCCESS) { + AUDIO_ERR_LOG("Stop call server failed:%{public}u", ret); + return false; + } + + if (renderMode_ == RENDER_MODE_CALLBACK) { + state_ = STOPPING; + AUDIO_INFO_LOG("Stop begin in callback mode sessionId %{public}d uid: %{public}d", sessionId_, clientUid_); + } + + std::unique_lock waitLock(callServerMutex_); + bool stopWaiting = callServerCV_.wait_for(waitLock, std::chrono::milliseconds(OPERATION_TIMEOUT_IN_MS), [this] { + return notifiedOperation_ == STOP_STREAM; // will be false when got notified. + }); + + if (notifiedOperation_ != STOP_STREAM || notifiedResult_ != SUCCESS) { + AUDIO_ERR_LOG("Stop failed: %{public}s Operation:%{public}d result:%{public}" PRId64".", + (!stopWaiting ? "timeout" : "no timeout"), notifiedOperation_, notifiedResult_); + state_ = INVALID; + return false; + } + waitLock.unlock(); + + state_ = STOPPED; + statusLock.unlock(); + + // in plan: call HiSysEventWrite + callbackHandler_->SendCallbackEvent(STATE_CHANGE_EVENT, state_); + + AUDIO_INFO_LOG("Stop SUCCESS, sessionId: %{public}d, uid: %{public}d", sessionId_, clientUid_); + UpdateTracker("STOPPED"); + return true; +} + +bool RendererInClientInner::ReleaseAudioStream(bool releaseRunner) +{ + CHECK_AND_RETURN_RET_LOG(state_ != RELEASED, ERR_ILLEGAL_STATE, "Capturer stream is already released"); + Trace trace("RendererInClientInner::ReleaseAudioStream " + std::to_string(sessionId_)); + CHECK_AND_RETURN_RET_LOG(ipcStream_ != nullptr, false, "ipcStream is null"); + ipcStream_->Release(); + // lock_guard lock(statusMutex_) // no lock, call release in any case, include blocked case. + { + std::lock_guard runnerlock(runnerMutex_); + if (releaseRunner) { + AUDIO_INFO_LOG("runner remove"); + callbackHandler_->ReleaseEventRunner(); + runnerReleased_ = true; + } + } + + // clear write callback + if (renderMode_ == RENDER_MODE_CALLBACK) { + cbThreadReleased_ = true; // stop loop + if (cbBufferQueue_.IsEmpty()) { + cbBufferQueue_.PushNoWait({nullptr, 0, 0}); + } + cbThreadCv_.notify_all(); + if (callbackLoop_.joinable()) { + callbackLoop_.join(); + } + } + paramsIsSet_ = false; + state_ = RELEASED; + UpdateTracker("RELEASED"); + AUDIO_INFO_LOG("Release end, sessionId: %{public}d, uid: %{public}d", sessionId_, clientUid_); + return true; +} + +bool RendererInClientInner::FlushAudioStream() +{ + Trace trace("RendererInClientInner::FlushAudioStream " + std::to_string(sessionId_)); + std::unique_lock statusLock(statusMutex_); + if ((state_ != RUNNING) && (state_ != PAUSED) && (state_ != STOPPED)) { + AUDIO_ERR_LOG("Flush failed. Illegal state:%{public}u", state_.load()); + return false; + } + CHECK_AND_RETURN_RET_LOG(FlushRingCache() == SUCCESS, false, "Flush cache failed"); + + CHECK_AND_RETURN_RET_LOG(ipcStream_ != nullptr, false, "ipcStream is not inited!"); + int32_t ret = ipcStream_->Flush(); + if (ret != SUCCESS) { + AUDIO_ERR_LOG("Flush call server failed:%{public}u", ret); + return false; + } + std::unique_lock waitLock(callServerMutex_); + bool stopWaiting = callServerCV_.wait_for(waitLock, std::chrono::milliseconds(OPERATION_TIMEOUT_IN_MS), [this] { + return notifiedOperation_ == FLUSH_STREAM; // will be false when got notified. + }); + + if (notifiedOperation_ != FLUSH_STREAM || notifiedResult_ != SUCCESS) { + AUDIO_ERR_LOG("Flush failed: %{public}s Operation:%{public}d result:%{public}" PRId64".", + (!stopWaiting ? "timeout" : "no timeout"), notifiedOperation_, notifiedResult_); + return false; + } + waitLock.unlock(); + AUDIO_INFO_LOG("Flush stream SUCCESS, sessionId: %{public}d", sessionId_); + return true; +} + +int32_t RendererInClientInner::FlushRingCache() +{ + ringCache_->ResetBuffer(); + return SUCCESS; +} + +int32_t RendererInClientInner::DrainRingCache() +{ + // send all data in ringCache_ to server even if GetReadableSize() < clientSpanSizeInByte_. + Trace trace("RendererInClientInner::DrainRingCache " + std::to_string(sessionId_)); + std::lock_guard lock(writeMutex_); + OptResult result = ringCache_->GetReadableSize(); + CHECK_AND_RETURN_RET_LOG(result.ret == OPERATION_SUCCESS, ERR_OPERATION_FAILED, "ring cache unreadable"); + size_t readableSize = result.size; + if (readableSize == 0) { + AUDIO_WARNING_LOG("Readable size is already zero"); + return SUCCESS; + } + + BufferDesc desc = {}; + uint64_t curWriteIndex = clientBuffer_->GetCurWriteFrame(); + int32_t ret = clientBuffer_->GetWriteBuffer(curWriteIndex, desc); + CHECK_AND_RETURN_RET_LOG(ret == SUCCESS, ERR_OPERATION_FAILED, "GetWriteBuffer failed %{public}d", ret); + + // if readableSize < clientSpanSizeInByte_, server will recv a data with some empty data. + // it looks like this: |*******_____| + size_t minSize = std::min(readableSize, clientSpanSizeInByte_); + result = ringCache_->Dequeue({desc.buffer, minSize}); + CHECK_AND_RETURN_RET_LOG(result.ret == OPERATION_SUCCESS, ERROR, "ringCache Dequeue failed %{public}d", result.ret); + clientBuffer_->SetCurWriteFrame(curWriteIndex + spanSizeInFrame_); + ipcStream_->UpdatePosition(); // notiify server update position + HandleRendererPositionChanges(minSize); + return SUCCESS; +} + +bool RendererInClientInner::DrainAudioStream() +{ + Trace trace("RendererInClientInner::DrainAudioStream " + std::to_string(sessionId_)); + std::unique_lock statusLock(statusMutex_); + if (state_ != RUNNING) { + AUDIO_ERR_LOG("Drain failed. Illegal state:%{public}u", state_.load()); + return false; + } + statusLock.unlock(); + + CHECK_AND_RETURN_RET_LOG(DrainRingCache() == SUCCESS, false, "Drain cache failed"); + + CHECK_AND_RETURN_RET_LOG(ipcStream_ != nullptr, false, "ipcStream is not inited!"); + int32_t ret = ipcStream_->Drain(); + if (ret != SUCCESS) { + AUDIO_ERR_LOG("Drain call server failed:%{public}u", ret); + return false; + } + std::unique_lock waitLock(callServerMutex_); + bool stopWaiting = callServerCV_.wait_for(waitLock, std::chrono::milliseconds(OPERATION_TIMEOUT_IN_MS), [this] { + return notifiedOperation_ == DRAIN_STREAM; // will be false when got notified. + }); + + if (notifiedOperation_ != DRAIN_STREAM || notifiedResult_ != SUCCESS) { + AUDIO_ERR_LOG("Drain failed: %{public}s Operation:%{public}d result:%{public}" PRId64".", + (!stopWaiting ? "timeout" : "no timeout"), notifiedOperation_, notifiedResult_); + return false; + } + waitLock.unlock(); + AUDIO_INFO_LOG("Drain stream SUCCESS, sessionId: %{public}d", sessionId_); + return true; +} + +void RendererInClientInner::SetPreferredFrameSize(int32_t frameSize) +{ + AUDIO_WARNING_LOG("Not Supported Yet"); +} + +int32_t RendererInClientInner::Write(uint8_t *pcmBuffer, size_t pcmBufferSize, uint8_t *metaBuffer, + size_t metaBufferSize) +{ + AUDIO_ERR_LOG("Write with metaBuffer is not supported"); + return ERR_INVALID_OPERATION; +} + +int32_t RendererInClientInner::Write(uint8_t *buffer, size_t bufferSize) +{ + CHECK_AND_RETURN_RET_LOG(renderMode_ != RENDER_MODE_CALLBACK, ERR_INCORRECT_MODE, "Not support mode"); + + Trace trace("RendererInClient::Write " + std::to_string(bufferSize)); + CHECK_AND_RETURN_RET_LOG(buffer != nullptr && bufferSize < MAX_WRITE_SIZE, ERR_INVALID_PARAM, + "invalid size is %{public}zu", bufferSize); + + std::lock_guard lock(writeMutex_); + + // if first call, call set thread priority. if thread tid change recall set thread priority + if (needSetThreadPriority_) { + AudioSystemManager::GetInstance()->RequestThreadPriority(gettid()); + needSetThreadPriority_ = false; + } + + std::unique_lock statusLock(statusMutex_); // status check + CHECK_AND_RETURN_RET_LOG(state_ == RUNNING, ERR_ILLEGAL_STATE, "Write: Illegal state:%{public}u", state_.load()); + statusLock.unlock(); + + // hold lock + if (isBlendSet_) { + audioBlend_.Process(buffer, bufferSize); + } + + size_t targetSize = bufferSize; + size_t offset = 0; + while (targetSize > sizePerFrameInByte_) { + // 1. write data into ring cache + OptResult result = ringCache_->GetWritableSize(); + CHECK_AND_RETURN_RET_LOG(result.ret == OPERATION_SUCCESS, bufferSize - targetSize, "RingCache write status " + "invalid size is:%{public}zu", result.size); + + size_t writableSize = result.size; + Trace::Count("RendererInClient::CacheBuffer->writableSize", writableSize); + + size_t writeSize = std::min(writableSize, targetSize); + BufferWrap bufferWrap = {buffer + offset, writeSize}; + + if (writeSize > 0) { + result = ringCache_->Enqueue(bufferWrap); + if (result.ret != OPERATION_SUCCESS) { + // in plan: recall enqueue in some cases + AUDIO_ERR_LOG("RingCache Enqueue failed ret:%{public}d size:%{public}zu", result.ret, result.size); + break; + } + offset += writeSize; + targetSize -= writeSize; + clientWrittenBytes_ += writeSize; + } + + // 2. copy data from cache to OHAudioBuffer + result = ringCache_->GetReadableSize(); + CHECK_AND_RETURN_RET_LOG(result.ret == OPERATION_SUCCESS, bufferSize - targetSize, "RingCache read status " + "invalid size is:%{public}zu", result.size); + size_t readableSize = result.size; + Trace::Count("RendererInClient::CacheBuffer->readableSize", readableSize); + + if (readableSize < clientSpanSizeInByte_) { + continue; + } + // if readable size is enough, we will call write data to server + int32_t ret = WriteCacheData(); + CHECK_AND_RETURN_RET_LOG(ret == SUCCESS, ERROR, "WriteCacheData failed %{public}d", ret); + } + + return bufferSize - targetSize; +} + +int32_t RendererInClientInner::WriteCacheData() +{ + int32_t sizeInFrame = clientBuffer_->GetAvailableDataFrames(); + CHECK_AND_RETURN_RET_LOG(sizeInFrame >= 0, ERROR, "GetAvailableDataFrames invalid, %{public}d", sizeInFrame); + if (sizeInFrame * sizePerFrameInByte_ < clientSpanSizeInByte_) { + // wait for server read some data + std::unique_lock lock(writeDataMutex_); + std::cv_status stat = writeDataCV_.wait_for(lock, std::chrono::milliseconds(OPERATION_TIMEOUT_IN_MS)); + CHECK_AND_RETURN_RET_LOG(stat == std::cv_status::no_timeout, ERROR, "write data time out"); + } + BufferDesc desc = {}; + uint64_t curWriteIndex = clientBuffer_->GetCurWriteFrame(); + int32_t ret = clientBuffer_->GetWriteBuffer(curWriteIndex, desc); + CHECK_AND_RETURN_RET_LOG(ret == SUCCESS, ERROR, "GetWriteBuffer failed %{public}d", ret); + OptResult result = ringCache_->Dequeue({desc.buffer, desc.bufLength}); + CHECK_AND_RETURN_RET_LOG(result.ret == OPERATION_SUCCESS, ERROR, "ringCache Dequeue failed %{public}d", result.ret); + AUDIO_DEBUG_LOG("RendererInClientInner::WriteCacheData() curWriteIndex[%{public}" PRIu64 "], spanSizeInFrame_ " + "%{public}u", curWriteIndex, spanSizeInFrame_); + clientBuffer_->SetCurWriteFrame(curWriteIndex + spanSizeInFrame_); + ipcStream_->UpdatePosition(); // notiify server update position + HandleRendererPositionChanges(desc.bufLength); + return SUCCESS; +} + +void RendererInClientInner::HandleRendererPositionChanges(size_t bytesWritten) +{ + totalBytesWritten_ += bytesWritten; + if (sizePerFrameInByte_ == 0) { + AUDIO_ERR_LOG("HandleRendererPositionChanges: sizePerFrameInByte_ is 0"); + return; + } + int64_t writtenFrameNumber = totalBytesWritten_ / sizePerFrameInByte_; + AUDIO_DEBUG_LOG("frame size: %{public}zu", sizePerFrameInByte_); + + { + std::lock_guard lock(markReachMutex_); + if (!rendererMarkReached_) { + AUDIO_DEBUG_LOG("Frame mark position: %{public}" PRId64", Total frames written: %{public}" PRId64, + static_cast(rendererMarkPosition_), static_cast(writtenFrameNumber)); + if (writtenFrameNumber >= rendererMarkPosition_) { + AUDIO_DEBUG_LOG("OnMarkReached %{public}" PRId64".", rendererMarkPosition_); + SendRenderMarkReachedEvent(rendererMarkPosition_); + rendererMarkReached_ = true; + } + } + } + + { + std::lock_guard lock(periodReachMutex_); + rendererPeriodWritten_ += (totalBytesWritten_ / sizePerFrameInByte_); + AUDIO_DEBUG_LOG("Frame period number: %{public}" PRId64", Total frames written: %{public}" PRId64, + static_cast(rendererPeriodSize_), static_cast(rendererPeriodWritten_)); + if (rendererPeriodWritten_ >= rendererPeriodSize_) { + rendererPeriodWritten_ %= rendererPeriodSize_; + AUDIO_DEBUG_LOG("OnPeriodReached, remaining frames: %{public}" PRId64, + static_cast(rendererPeriodWritten_)); + SendRenderPeriodReachedEvent(rendererPeriodSize_); + } + } +} + +// OnRenderMarkReach by eventHandler +void RendererInClientInner::SendRenderMarkReachedEvent(int64_t rendererMarkPosition) +{ + std::lock_guard runnerlock(runnerMutex_); + if (runnerReleased_) { + AUDIO_WARNING_LOG("SendRenderMarkReachedEvent runner released"); + return; + } + callbackHandler_->SendCallbackEvent(RENDERER_MARK_REACHED_EVENT, rendererMarkPosition); +} + +// OnRenderPeriodReach by eventHandler +void RendererInClientInner::SendRenderPeriodReachedEvent(int64_t rendererPeriodSize) +{ + std::lock_guard runnerlock(runnerMutex_); + if (runnerReleased_) { + AUDIO_WARNING_LOG("SendRenderPeriodReachedEvent runner released"); + return; + } + callbackHandler_->SendCallbackEvent(RENDERER_PERIOD_REACHED_EVENT, rendererPeriodSize); +} + +int32_t RendererInClientInner::ParamsToStateCmdType(int64_t params, State &state, StateChangeCmdType &cmdType) +{ + cmdType = CMD_FROM_CLIENT; + switch (params) { + case HANDLER_PARAM_NEW: + state = NEW; + break; + case HANDLER_PARAM_PREPARED: + state = PREPARED; + break; + case HANDLER_PARAM_RUNNING: + state = RUNNING; + break; + case HANDLER_PARAM_STOPPED: + state = STOPPED; + break; + case HANDLER_PARAM_RELEASED: + state = RELEASED; + break; + case HANDLER_PARAM_PAUSED: + state = PAUSED; + break; + case HANDLER_PARAM_STOPPING: + state = STOPPING; + break; + case HANDLER_PARAM_RUNNING_FROM_SYSTEM: + state = RUNNING; + cmdType = CMD_FROM_SYSTEM; + break; + case HANDLER_PARAM_PAUSED_FROM_SYSTEM: + state = PAUSED; + cmdType = CMD_FROM_SYSTEM; + break; + default: + state = INVALID; + break; + } + return SUCCESS; +} + +int32_t RendererInClientInner::StateCmdTypeToParams(int64_t ¶ms, State state, StateChangeCmdType cmdType) +{ + if (cmdType == CMD_FROM_CLIENT) { + params = static_cast(state); + return SUCCESS; + } + switch (state) { + case RUNNING: + params = HANDLER_PARAM_RUNNING_FROM_SYSTEM; + break; + case PAUSED: + params = HANDLER_PARAM_PAUSED_FROM_SYSTEM; + break; + default: + params = HANDLER_PARAM_INVALID; + break; + } + return SUCCESS; +} + +int32_t RendererInClientInner::Read(uint8_t &buffer, size_t userSize, bool isBlockingRead) +{ + AUDIO_ERR_LOG("Read is not supported"); + return ERROR; +} + + +uint32_t RendererInClientInner::GetUnderflowCount() +{ + return underrunCount_; +} + +void RendererInClientInner::SetRendererPositionCallback(int64_t markPosition, + const std::shared_ptr &callback) +{ + // waiting for review + std::lock_guard lock(markReachMutex_); + CHECK_AND_RETURN_LOG(callback != nullptr, "RendererPositionCallback is nullptr"); + rendererPositionCallback_ = callback; + rendererMarkPosition_ = markPosition; + rendererMarkReached_ = false; +} + +void RendererInClientInner::UnsetRendererPositionCallback() +{ + // waiting for review + std::lock_guard lock(markReachMutex_); + rendererPositionCallback_ = nullptr; + rendererMarkPosition_ = 0; + rendererMarkReached_ = false; +} + +void RendererInClientInner::SetRendererPeriodPositionCallback(int64_t markPosition, + const std::shared_ptr &callback) +{ + // waiting for review + std::lock_guard lock(periodReachMutex_); + CHECK_AND_RETURN_LOG(callback != nullptr, "RendererPeriodPositionCallback is nullptr"); + rendererPeriodPositionCallback_ = callback; + rendererPeriodSize_ = 0; + totalBytesWritten_ = 0; + rendererPeriodWritten_ = 0; +} + +void RendererInClientInner::UnsetRendererPeriodPositionCallback() +{ + // waiting for review + std::lock_guard lock(periodReachMutex_); + rendererPeriodPositionCallback_ = nullptr; + rendererPeriodSize_ = 0; + totalBytesWritten_ = 0; + rendererPeriodWritten_ = 0; +} + +void RendererInClientInner::SetCapturerPositionCallback(int64_t markPosition, + const std::shared_ptr &callback) +{ + AUDIO_ERR_LOG("SetCapturerPositionCallback is not supported"); + return; +} + +void RendererInClientInner::UnsetCapturerPositionCallback() +{ + AUDIO_ERR_LOG("SetCapturerPositionCallback is not supported"); + return; +} + +void RendererInClientInner::SetCapturerPeriodPositionCallback(int64_t markPosition, + const std::shared_ptr &callback) +{ + AUDIO_ERR_LOG("SetCapturerPositionCallback is not supported"); + return; +} + +void RendererInClientInner::UnsetCapturerPeriodPositionCallback() +{ + AUDIO_ERR_LOG("SetCapturerPositionCallback is not supported"); + return; +} + +int32_t RendererInClientInner::SetRendererSamplingRate(uint32_t sampleRate) +{ + AUDIO_ERR_LOG("SetRendererSamplingRate to %{publid}d is not supported", sampleRate); + return ERROR; +} + +uint32_t RendererInClientInner::GetRendererSamplingRate() +{ + return streamParams_.samplingRate; +} + +int32_t RendererInClientInner::SetBufferSizeInMsec(int32_t bufferSizeInMsec) +{ + // bufferSizeInMsec is checked between 5ms and 20ms. + bufferSizeInMsec_ = bufferSizeInMsec; + AUDIO_INFO_LOG("SetBufferSizeInMsec to %{publid}d", bufferSizeInMsec_); + if (renderMode_ == RENDER_MODE_CALLBACK) { + uint64_t bufferDurationInUs = bufferSizeInMsec_ * AUDIO_US_PER_MS; + InitCallbackBuffer(bufferDurationInUs); + } + return SUCCESS; +} + +void RendererInClientInner::SetApplicationCachePath(const std::string cachePath) +{ + cachePath_ = cachePath; + AUDIO_INFO_LOG("SetApplicationCachePath to %{publid}s", cachePath_.c_str()); +} + +int32_t RendererInClientInner::SetChannelBlendMode(ChannelBlendMode blendMode) +{ + if ((state_ != PREPARED) && (state_ != NEW)) { + AUDIO_ERR_LOG("SetChannelBlendMode in invalid status:%{public}d", state_.load()); + return ERR_ILLEGAL_STATE; + } + isBlendSet_ = true; + audioBlend_.SetParams(blendMode, streamParams_.format, streamParams_.channels); + return SUCCESS; +} + +int32_t RendererInClientInner::SetVolumeWithRamp(float volume, int32_t duration) +{ + // in plan + return ERROR; +} + +void RendererInClientInner::SetStreamTrackerState(bool trackerRegisteredState) +{ + streamTrackerRegistered_ = trackerRegisteredState; +} + +void RendererInClientInner::GetSwitchInfo(IAudioStream::SwitchInfo& info) +{ + // in plan +} + +IAudioStream::StreamClass RendererInClientInner::GetStreamClass() +{ + return PA_STREAM; +} +} // namespace AudioStandard +} // namespace OHOS +#endif // FAST_AUDIO_STREAM_H diff --git a/services/audio_service/common/include/audio_ring_cache.h b/services/audio_service/common/include/audio_ring_cache.h new file mode 100644 index 0000000000000000000000000000000000000000..40b018cdc92c2d5e532f5a496369e0ee6cb7f06d --- /dev/null +++ b/services/audio_service/common/include/audio_ring_cache.h @@ -0,0 +1,95 @@ +/* + * 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 AUDIO_RING_CACHE_H +#define AUDIO_RING_CACHE_H + +#include "stdint.h" +#include +#include +#include +#include + +namespace OHOS { +namespace AudioStandard { +// in plan: Using a globally unified buffer description in AudioStandard namesapce. +struct BufferWrap { + uint8_t *dataPtr = nullptr; + size_t dataSize = 0; +}; +enum RetType : uint32_t { + OPERATION_SUCCESS = 0, + OPERATION_FAILED, + INDEX_OUT_OF_RANGE, + DATA_INSUFFICIENT, + INVALID_STATUS, + INVALID_OPERATION, + INVALID_PARAMS, +}; +struct OptResult { + RetType ret = INVALID_OPERATION; + size_t size = 0; +}; + +/** + * AudioRingCache itself is thread safe, but you must be careful when combining calls to GetWriteableSize and Enqueue. + * As the actual writable size may have changed when Enqueue is called. In this case, enqueue will return a error, and + * you need to handle it. The combination of calling GetReadableSize and Dequeue all also requires handling this issue. +*/ +class AudioRingCache { +public: + static std::unique_ptr Create(size_t cacheSize); + AudioRingCache(size_t cacheSize); + ~AudioRingCache(); + + OptResult ReConfig(size_t cacheSize, bool copyRemained = true); + + // This operation will clear the buffer and reset inner read/write index. + void ResetBuffer(); + + size_t GetCahceSize(); + + // Get the buffer size that can be written. 0 <= WritableSize <= cacheTotalSize_ + OptResult GetWritableSize(); + + // Get the buffer size that can be read. 0 <= ReadableSize <= cacheTotalSize_ + OptResult GetReadableSize(); + + // Call GetWritableSize first, than call Enqueue with valid buffer size that <= WritableSize. + // Call Enqueue will move write index ahead. + OptResult Enqueue(const BufferWrap &buffer); + + // Call GetReadableSize first, than call Dequeue with valid buffer size that <= ReadableSize. + // Call Dequeue will move read index ahead, together with inner base index ahead. + OptResult Dequeue(const BufferWrap &buffer); + +private: + bool Init(); + OptResult GetWritableSizeNoLock(); + OptResult GetReadableSizeNoLock(); + OptResult HandleCrossDequeue(size_t tempReadIndex, size_t readableSize, const BufferWrap &buffer); + void ReIndex(); +private: + std::mutex cacheMutex_; + std::unique_ptr basePtr_; + size_t cacheTotalSize_ = 0; + + size_t baseIndex_ = 0; + size_t writeIndex_ = 0; + size_t readIndex_ = 0; +}; +} // namespace AudioStandard +} // namespace OHOS +#endif // AUDIO_RING_CACHE_H diff --git a/services/audio_service/common/include/i_stream_listener.h b/services/audio_service/common/include/i_stream_listener.h new file mode 100644 index 0000000000000000000000000000000000000000..1889aaa834e8e84436318ce743fe634bd4dd795a --- /dev/null +++ b/services/audio_service/common/include/i_stream_listener.h @@ -0,0 +1,43 @@ +/* + * 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 I_STREAM_LISTENER_H +#define I_STREAM_LISTENER_H + +#include "audio_info.h" + +namespace OHOS { +namespace AudioStandard { +enum Operation : int32_t { + START_STREAM = 0, + PAUSE_STREAM, + STOP_STREAM, + RELEASE_STREAM, + FLUSH_STREAM, + DRAIN_STREAM, + UPDATE_STREAM, // when server notify client index update + BUFFER_UNDERRUN, + BUFFER_OVERFLOW, + MAX_OPERATION_CODE // in plan add underrun overflow +}; +class IStreamListener { +public: + virtual ~IStreamListener() = default; + + virtual int32_t OnOperationHandled(Operation operation, int64_t result) = 0; +}; +} // namespace AudioStandard +} // namespace OHOS +#endif // I_STREAM_LISTENER_H diff --git a/services/audio_service/common/include/ipc_stream.h b/services/audio_service/common/include/ipc_stream.h new file mode 100644 index 0000000000000000000000000000000000000000..48dcf8ed3be7622519af8510904283371de48c9b --- /dev/null +++ b/services/audio_service/common/include/ipc_stream.h @@ -0,0 +1,118 @@ +/* + * 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 IPC_STREAM_H +#define IPC_STREAM_H + +#include + +#include "ipc_types.h" +#include "iremote_broker.h" +#include "iremote_proxy.h" +#include "iremote_stub.h" + +#include "audio_info.h" +#include "audio_process_config.h" +#include "i_stream_listener.h" +#include "oh_audio_buffer.h" + +namespace OHOS { +namespace AudioStandard { +class IpcStream : public IRemoteBroker { +public: + virtual ~IpcStream() = default; + + virtual int32_t RegisterStreamListener(sptr object) = 0; + + virtual int32_t ResolveBuffer(std::shared_ptr &buffer) = 0; + + virtual int32_t UpdatePosition() = 0; + + virtual int32_t GetAudioSessionID(uint32_t &sessionId) = 0; + + virtual int32_t Start() = 0; + + virtual int32_t Pause() = 0; + + virtual int32_t Stop() = 0; + + virtual int32_t Release() = 0; + + virtual int32_t Flush() = 0; + + virtual int32_t Drain() = 0; + + virtual int32_t GetAudioTime(uint64_t &framePos, uint64_t &timeStamp) = 0; + + virtual int32_t GetLatency(uint64_t &latency) = 0; + + virtual int32_t SetRate(int32_t rate) = 0; // SetRenderRate + + virtual int32_t GetRate(int32_t &rate) = 0; // SetRenderRate + + virtual int32_t SetLowPowerVolume(float volume) = 0; // renderer only + + virtual int32_t GetLowPowerVolume(float &volume) = 0; // renderer only + + virtual int32_t SetAudioEffectMode(int32_t effectMode) = 0; // renderer only + + virtual int32_t GetAudioEffectMode(int32_t &effectMode) = 0; // renderer only + + virtual int32_t SetPrivacyType(int32_t privacyType) = 0; // renderer only + + virtual int32_t GetPrivacyType(int32_t &privacyType) = 0; // renderer only + + // IPC code. + enum IpcStreamMsg : uint32_t { + ON_REGISTER_STREAM_LISTENER = 0, + ON_RESOLVE_BUFFER, + ON_UPDATE_POSITION, + ON_GET_AUDIO_SESSIONID, + ON_START, + ON_PAUSE, + ON_STOP, + ON_RELEASE, + ON_FLUSH, + ON_DRAIN, + OH_GET_AUDIO_TIME, + ON_GET_LATENCY, + ON_SET_RATE, + ON_GET_RATE, + ON_SET_LOWPOWER_VOLUME, + ON_GET_LOWPOWER_VOLUME, + ON_SET_EFFECT_MODE, + ON_GET_EFFECT_MODE, + ON_SET_PRIVACY_TYPE, + ON_GET_PRIVACY_TYPE, + IPC_STREAM_MAX_MSG + }; + + DECLARE_INTERFACE_DESCRIPTOR(u"IpcStream"); +}; + +class IpcStreamListener : public IRemoteBroker, public IStreamListener { +public: + virtual ~IpcStreamListener() = default; + + // IPC code. + enum IpcStreamListenerMsg : uint32_t { + ON_OPERATION_HANDLED = 0, + IPC_STREAM_LISTENER_MAX_MSG + }; + DECLARE_INTERFACE_DESCRIPTOR(u"IpcStreamListener"); +}; +} // namespace AudioStandard +} // namespace OHOS +#endif // IPC_STREAM_H diff --git a/services/audio_service/common/include/volume_tools.h b/services/audio_service/common/include/volume_tools.h new file mode 100644 index 0000000000000000000000000000000000000000..201ba23d16ef6036490e6285733f7495e6921ea7 --- /dev/null +++ b/services/audio_service/common/include/volume_tools.h @@ -0,0 +1,48 @@ +/* + * 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 VOLUME_TOOLS_H +#define VOLUME_TOOLS_H +#include "audio_info.h" + +namespace OHOS { +namespace AudioStandard { +static const size_t CHANNEL_MAX = 16; // same with CHANNEL_16 + +static const int32_t INT32_VOLUME_MIN = 0; // 0, min volume +static const uint32_t VOLUME_SHIFT = 16; +static constexpr int32_t INT32_VOLUME_MAX = 1 << VOLUME_SHIFT; // 1 << 16 = 65536, max volume +struct ChannelVolumes { + AudioChannel channel = STEREO; + int32_t volStart[CHANNEL_MAX]; + int32_t volEnd[CHANNEL_MAX]; +}; + +class VolumeTools { +public: + static bool IsVolumeValid(float volFloat); // 0.0 <= volFloat <= 1.0 + static bool IsVolumeValid(int32_t volInt); // 0 <= volInt <= 65536 + static bool IsVolumeValid(ChannelVolumes vols); + static int32_t GetInt32Vol(float volFloat); + static ChannelVolumes GetChannelVolumes(AudioChannel channel, int32_t volStart, int32_t volEnd); + static ChannelVolumes GetChannelVolumes(AudioChannel channel, float volStart, float volEnd); + + // Data size should be rounded to each sample size + // There will be significant sound quality loss when process uint8_t samples. + static int32_t Process(const BufferDesc &buffer, AudioSampleFormat format, ChannelVolumes vols); +}; +} // namespace AudioStandard +} // namespace OHOS +#endif // VOLUME_TOOLS_H \ No newline at end of file diff --git a/services/audio_service/common/src/audio_process_config.cpp b/services/audio_service/common/src/audio_process_config.cpp index 6ed503992117cbf512251237370478943d973756..9a22434b5aba3b2895d52002ac2ad279d9d7ef8c 100644 --- a/services/audio_service/common/src/audio_process_config.cpp +++ b/services/audio_service/common/src/audio_process_config.cpp @@ -34,6 +34,7 @@ int32_t ProcessConfig::WriteConfigToParcel(const AudioProcessConfig &config, Mes parcel.WriteInt32(config.streamInfo.encoding); parcel.WriteInt32(config.streamInfo.format); parcel.WriteInt32(config.streamInfo.channels); + parcel.WriteUint64(config.streamInfo.channelLayout); // AudioMode parcel.WriteInt32(config.audioMode); @@ -50,6 +51,11 @@ int32_t ProcessConfig::WriteConfigToParcel(const AudioProcessConfig &config, Mes // streamType parcel.WriteInt32(config.streamType); + // Recorder only + parcel.WriteBool(config.isInnerCapturer); + parcel.WriteBool(config.isWakeupCapturer); + // LYH waiting for review, capturerParam 还有layout + return SUCCESS; } @@ -65,6 +71,7 @@ int32_t ProcessConfig::ReadConfigFromParcel(AudioProcessConfig &config, MessageP config.streamInfo.encoding = static_cast(parcel.ReadInt32()); config.streamInfo.format = static_cast(parcel.ReadInt32()); config.streamInfo.channels = static_cast(parcel.ReadInt32()); + config.streamInfo.channelLayout = static_cast(parcel.ReadUint64()); // AudioMode config.audioMode = static_cast(parcel.ReadInt32()); @@ -81,6 +88,9 @@ int32_t ProcessConfig::ReadConfigFromParcel(AudioProcessConfig &config, MessageP // streamType config.streamType = static_cast(parcel.ReadInt32()); + // Recorder only + config.isInnerCapturer = parcel.ReadBool(); + config.isWakeupCapturer = parcel.ReadBool(); return SUCCESS; } diff --git a/services/audio_service/common/src/audio_ring_cache.cpp b/services/audio_service/common/src/audio_ring_cache.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1a70f7f8b120dd9c748e1349bdfaf2fce3744a67 --- /dev/null +++ b/services/audio_service/common/src/audio_ring_cache.cpp @@ -0,0 +1,317 @@ +/* + * 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 "audio_ring_cache.h" +#include "audio_log.h" + +#include "securec.h" + +namespace OHOS { +namespace AudioStandard { +namespace { +static const size_t MAX_CACHE_SIZE = 16 * 1024 * 1024; // 16M +static const size_t BASE_INDEX_FENCE = SIZE_MAX - 2 * MAX_CACHE_SIZE; +} +AudioRingCache::AudioRingCache(size_t cacheSize) : cacheTotalSize_(cacheSize) +{ + AUDIO_INFO_LOG("AudioRingCache() with cacheSize:%{public}zu", cacheSize); +} + +AudioRingCache::~AudioRingCache() +{ + AUDIO_DEBUG_LOG("~AudioRingCache()"); +} + +// Init is private and called in Create, not need lock. Call Init with lock in ReConfig +bool AudioRingCache::Init() +{ + if (cacheTotalSize_ > MAX_CACHE_SIZE) { + AUDIO_ERR_LOG("Init failed: size too large:%{public}zu", cacheTotalSize_); + return false; + } + baseIndex_ = 0; + writeIndex_ = 0; + readIndex_ = 0; + basePtr_ = std::make_unique(cacheTotalSize_); + if (basePtr_ == nullptr) { + AUDIO_ERR_LOG("Init failed, get memory failed size is:%{public}zu", cacheTotalSize_); + return false; + } + if (memset_s(basePtr_.get(), cacheTotalSize_, 0, cacheTotalSize_) != EOK) { + AUDIO_ERR_LOG("Init call memeset_s failed."); + return false; + } + return true; +} + +std::unique_ptr AudioRingCache::Create(size_t cacheSize) +{ + if (cacheSize > MAX_CACHE_SIZE) { + AUDIO_ERR_LOG("Create failed: size too large:%{public}zu", cacheSize); + return nullptr; + } + std::unique_ptr ringCache = std::make_unique(cacheSize); + + if (ringCache->Init() != true) { + AUDIO_ERR_LOG("Create failed: Init failed"); + return nullptr; + } + return ringCache; +} + +OptResult AudioRingCache::ReConfig(size_t cacheSize, bool copyRemained) +{ + AUDIO_INFO_LOG("ReConfig with cacheSize:%{public}zu", cacheSize); + OptResult result; + result.ret = OPERATION_SUCCESS; + result.size = cacheSize; + if (cacheSize > MAX_CACHE_SIZE) { + result.ret = INDEX_OUT_OF_RANGE; + AUDIO_ERR_LOG("ReConfig failed: size too large:%{public}zu", cacheSize); + return result; + } + if (!copyRemained) { + cacheTotalSize_ = cacheSize; + std::lock_guard lock(cacheMutex_); // need lock as we operation buffer in Init + if (Init() != true) { + result.ret = OPERATION_FAILED; + return result; + } + AUDIO_INFO_LOG("ReConfig success cacheSize:%{public}zu", cacheSize); + return result; + } + // if need copyRemained, we should check the cacheSize >= remained size. + result = GetReadableSize(); + if (result.ret != OPERATION_SUCCESS || result.size > cacheSize) { + AUDIO_ERR_LOG("ReConfig in copyRemained failed ret:%{public}d size :%{public}zu", result.ret, cacheSize); + return result; + } + std::unique_ptr temp = std::make_unique(result.size); + result = Dequeue({temp.get(), result.size}); + CHECK_AND_RETURN_RET_LOG(result.ret == OPERATION_SUCCESS, result, + "ReConfig dequeue failed ret:%{public}d", result.ret); + std::unique_lock uniqueLock(cacheMutex_); + cacheTotalSize_ = cacheSize; + if (Init() != true) { + result.ret = OPERATION_FAILED; + return result; + } + uniqueLock.unlock(); // unlock as Enqueue will lock + result = Enqueue({temp.get(), result.size}); + + return result; +} + +void AudioRingCache::ResetBuffer() +{ + std::lock_guard lock(cacheMutex_); + baseIndex_ = 0; + writeIndex_ = 0; + readIndex_ = 0; + if (memset_s(basePtr_.get(), cacheTotalSize_, 0, cacheTotalSize_) != EOK) { + AUDIO_ERR_LOG("ResetBuffer call memeset_s failed."); + } +} + +size_t AudioRingCache::GetCahceSize() +{ + std::lock_guard lock(cacheMutex_); + return cacheTotalSize_; +} + +OptResult AudioRingCache::GetWritableSize() +{ + std::lock_guard lock(cacheMutex_); + return GetWritableSizeNoLock(); +} + +// call this with cacheMutex_ +OptResult AudioRingCache::GetWritableSizeNoLock() +{ + OptResult result; + if (writeIndex_ < readIndex_ || writeIndex_ - readIndex_ > cacheTotalSize_) { + result.ret = INVALID_STATUS; + result.size = 0; + AUDIO_ERR_LOG("GetWritableSize failed: writeIndex_[%{public}zu] readIndex_[%{public}zu]", + writeIndex_, readIndex_); + return result; + } + result.size = cacheTotalSize_ - (writeIndex_ - readIndex_); + result.ret = OPERATION_SUCCESS; + return result; +} + +OptResult AudioRingCache::GetReadableSize() +{ + std::lock_guard lock(cacheMutex_); + return GetReadableSizeNoLock(); +} + +// call this with cacheMutex_ +OptResult AudioRingCache::GetReadableSizeNoLock() +{ + OptResult result; + if (writeIndex_ < readIndex_ || writeIndex_ - readIndex_ > cacheTotalSize_) { + result.ret = INVALID_STATUS; + result.size = 0; + AUDIO_ERR_LOG("GetReadableSize failed: writeIndex_[%{public}zu] readIndex_[%{public}zu]", + writeIndex_, readIndex_); + return result; + } + result.size = writeIndex_ - readIndex_; + result.ret = OPERATION_SUCCESS; + return result; +} + +OptResult AudioRingCache::Enqueue(const BufferWrap &buffer) +{ + std::lock_guard lock(cacheMutex_); + OptResult result; + // params check + if (buffer.dataPtr == nullptr || buffer.dataSize > MAX_CACHE_SIZE || buffer.dataSize == 0) { + result.ret = INVALID_PARAMS; + AUDIO_ERR_LOG("Enqueue failed: BufferWrap is null or size %{public}zu is too large", buffer.dataSize); + return result; + } + + // Get writable size here,do not directly call GetWriteableSize() as it will cause a deadlock. + result = GetWritableSizeNoLock(); + CHECK_AND_RETURN_RET_LOG(result.ret == OPERATION_SUCCESS, result, "Enqueue failed to get writeable size."); + size_t writableSize = result.size; + + if (buffer.dataSize > writableSize) { + result = {INDEX_OUT_OF_RANGE, writableSize}; + AUDIO_WARNING_LOG("Enqueue find buffer not enough, writableSize:%{public}zu , enqueue size:%{public}zu", + writableSize, buffer.dataSize); + return result; + } + // buffer.dataSize <= writableSize, let's do memory copy. + // judge if cross buffer + size_t tempWriteIndex = writeIndex_ + buffer.dataSize; + if (writeIndex_ < baseIndex_ + cacheTotalSize_ && tempWriteIndex > baseIndex_ + cacheTotalSize_) { + size_t headSize = baseIndex_ + cacheTotalSize_ - writeIndex_; + size_t tailSize = tempWriteIndex - (baseIndex_ + cacheTotalSize_); + void *headPtr = static_cast(basePtr_.get() + (writeIndex_ - baseIndex_)); + void *tailPtr = static_cast(basePtr_.get()); + if ((memcpy_s(headPtr, headSize, static_cast(buffer.dataPtr), headSize)) == EOK && + memcpy_s(tailPtr, tailSize, static_cast(buffer.dataPtr + headSize), tailSize) == EOK) { + writeIndex_ = tempWriteIndex; // move write index + result = {OPERATION_SUCCESS, buffer.dataSize}; + return result; + } + result = {OPERATION_FAILED, writableSize}; + AUDIO_ERR_LOG("Enqueue memcpy_s failed: writeIndex_[%{public}zu] baseIndex_[%{public}zu] buffer.dataSize" + "[%{public}zu]", writeIndex_, baseIndex_, buffer.dataSize); + return result; + } + // not cross + size_t offset = writeIndex_ >= baseIndex_ + cacheTotalSize_ ? (writeIndex_ - baseIndex_ - cacheTotalSize_) : + (writeIndex_ - baseIndex_); + void *writePtr = static_cast(basePtr_.get() + offset); + if ((memcpy_s(writePtr, buffer.dataSize, static_cast(buffer.dataPtr), buffer.dataSize)) == EOK) { + writeIndex_ = tempWriteIndex; // move write index + result = {OPERATION_SUCCESS, buffer.dataSize}; + return result; + } + AUDIO_ERR_LOG("Enqueue memcpy_s failed: writeIndex_[%{public}zu] baseIndex_[%{public}zu] buffer.dataSize" + "[%{public}zu]", writeIndex_, baseIndex_, buffer.dataSize); + result = {OPERATION_FAILED, writableSize}; + return result; +} + +void AudioRingCache::ReIndex() +{ + AUDIO_INFO_LOG("ReIndex baseIndex[%{public}zu] readIndex[%{public}zu] writeIndex[%{public}zu]", baseIndex_, + readIndex_, writeIndex_); + writeIndex_ -= baseIndex_; + readIndex_ -= baseIndex_; + baseIndex_ = 0; +} +OptResult AudioRingCache::HandleCrossDequeue(size_t tempReadIndex, size_t readableSize, const BufferWrap &buffer) +{ + OptResult result; + // cross + size_t headSize = baseIndex_ + cacheTotalSize_ - readIndex_; + size_t tailSize = tempReadIndex - (baseIndex_ + cacheTotalSize_); + void *headPtr = static_cast(basePtr_.get() + (readIndex_ - baseIndex_)); + void *tailPtr = static_cast(basePtr_.get()); + if (memcpy_s(static_cast(buffer.dataPtr), headSize, headPtr, headSize) != EOK || + memcpy_s(static_cast(buffer.dataPtr + headSize), tailSize, tailPtr, tailSize) != EOK) { + result = {OPERATION_FAILED, readableSize}; + AUDIO_ERR_LOG("Dequeue memcpy_s failed: readIndex[%{public}zu] baseIndex_[%{public}zu] buffer.dataSize" + "[%{public}zu]", readIndex_, baseIndex_, buffer.dataSize); + return result; + } + CHECK_AND_BREAK_LOG(memset_s(headPtr, headSize, 0, headSize) == EOK, "reset headPtr fail."); + CHECK_AND_BREAK_LOG(memset_s(tailPtr, tailSize, 0, tailSize) == EOK, "reset headPtr fail."); + + readIndex_ = tempReadIndex; // move write index + baseIndex_ += cacheTotalSize_; // move base index + if (baseIndex_ >= BASE_INDEX_FENCE) { + ReIndex(); + } + result = {OPERATION_SUCCESS, buffer.dataSize}; + return result; +} + +OptResult AudioRingCache::Dequeue(const BufferWrap &buffer) +{ + std::lock_guard lock(cacheMutex_); + OptResult result; + // params check + if (buffer.dataPtr == nullptr || buffer.dataSize > MAX_CACHE_SIZE) { + result.ret = INVALID_PARAMS; + AUDIO_ERR_LOG("Dequeue failed: BufferWrap is null or size %{public}zu is too large", buffer.dataSize); + return result; + } + + result = GetReadableSizeNoLock(); + CHECK_AND_RETURN_RET_LOG(result.ret == OPERATION_SUCCESS, result, "Dequeue failed to get readable size."); + size_t readableSize = result.size; + if (buffer.dataSize > readableSize) { + result = {INVALID_OPERATION, readableSize}; + AUDIO_WARNING_LOG("Dequeue find buffer not enough, readableSize:%{public}zu , Dequeue size:%{public}zu", + readableSize, buffer.dataSize); + return result; + } + + // buffer.dataSize <= readableSize, let's do memory copy. + // judge if cross buffer + size_t tempReadIndex = readIndex_ + buffer.dataSize; + if (tempReadIndex > baseIndex_ + cacheTotalSize_) { + return HandleCrossDequeue(tempReadIndex, readableSize, buffer); + } + + // not cross + void *readPtr = static_cast(basePtr_.get() + readIndex_ - baseIndex_); + if ((memcpy_s(static_cast(buffer.dataPtr), buffer.dataSize, readPtr, buffer.dataSize)) != EOK) { + AUDIO_ERR_LOG("Dequeue memcpy_s failed: readIndex_[%{public}zu] baseIndex_[%{public}zu] buffer.dataSize" + "[%{public}zu]", readIndex_, baseIndex_, buffer.dataSize); + result = {OPERATION_FAILED, readableSize}; + return result; + } + CHECK_AND_BREAK_LOG(memset_s(readPtr, buffer.dataSize, 0, buffer.dataSize) == EOK, "reset readPtr fail."); + if (tempReadIndex - baseIndex_ == cacheTotalSize_) { + baseIndex_ += cacheTotalSize_; + } + readIndex_ = tempReadIndex; // move read index + if (baseIndex_ >= BASE_INDEX_FENCE) { + ReIndex(); + } + result = {OPERATION_SUCCESS, buffer.dataSize}; + return result; +} +} // namespace AudioStandard +} // namespace OHOS diff --git a/services/audio_service/common/src/oh_audio_buffer.cpp b/services/audio_service/common/src/oh_audio_buffer.cpp index 649bb85188087ab38304a7421aa529c942b7ec38..d3edcf498d538c0e36657c2ae93c761ac97402e2 100644 --- a/services/audio_service/common/src/oh_audio_buffer.cpp +++ b/services/audio_service/common/src/oh_audio_buffer.cpp @@ -480,7 +480,7 @@ int32_t OHAudioBuffer::SetCurWriteFrame(uint64_t writeFrame) // check new pos in (read + cache) range: read ~ read + totalSize - 1*spanSize uint64_t curRead = basicBufferInfo_->curReadFrame.load(); - if (writeFrame < curRead || writeFrame - curRead > totalSizeInFrame_ - spanSizeInFrame_) { + if (writeFrame < curRead || writeFrame - curRead > totalSizeInFrame_) { AUDIO_ERR_LOG("Invalid writeFrame %{public}" PRIu64" out of cache range, curRead %{public}" PRIu64".", writeFrame, curRead); return ERR_INVALID_PARAM; diff --git a/services/audio_service/common/src/volume_tools.cpp b/services/audio_service/common/src/volume_tools.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7695478485c93a6548733bb0a4c0747b2b1258db --- /dev/null +++ b/services/audio_service/common/src/volume_tools.cpp @@ -0,0 +1,220 @@ +/* + * 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 "volume_tools.h" +#include "audio_errors.h" +#include "audio_log.h" + +namespace { +static const int32_t UINT8_SHIFT = 0x80; +static const int32_t INT24_SHIFT = 8; +static const uint32_t SHIFT_EIGHT = 8; +static const uint32_t SHIFT_SIXTEEN = 16; +static const uint32_t ARRAY_INDEX_TWO = 2; +static const size_t MIN_FRAME_SIZE = 1; +} +namespace OHOS { +namespace AudioStandard { +bool VolumeTools::IsVolumeValid(float volFloat) +{ + return volFloat >= 0.0 && volFloat <= 1.0; +} + +bool VolumeTools::IsVolumeValid(int32_t volInt) +{ + return volInt >= INT32_VOLUME_MIN && volInt <= INT32_VOLUME_MAX; +} +bool VolumeTools::IsVolumeValid(ChannelVolumes vols) +{ + if (vols.channel > CHANNEL_16 || vols.channel < MONO) { + return false; + } + for (size_t i = 0; i < vols.channel; i++) { + if (!IsVolumeValid(vols.volStart[i]) || !IsVolumeValid(vols.volEnd[i])) { + return false; + } + } + + return true; +} + +int32_t VolumeTools::GetInt32Vol(float volFloat) +{ + if (IsVolumeValid(volFloat)) { + return volFloat * INT32_VOLUME_MAX; + } + if (volFloat < 0.0) { + return INT32_VOLUME_MIN; + } + return INT32_VOLUME_MAX; +} + +ChannelVolumes VolumeTools::GetChannelVolumes(AudioChannel channel, int32_t volStart, int32_t volEnd) +{ + ChannelVolumes vols = {}; + if (!IsVolumeValid(volStart) || !IsVolumeValid(volEnd) || channel > CHANNEL_16 || channel < MONO) { + AUDIO_ERR_LOG("GetChannelVolumes failed with invalid vol:%{public}d %{public}d channel: %{public}d", volStart, + volEnd, channel); + return vols; + } + for (size_t i = 0; i < channel; i++) { + vols.volStart[i] = volStart; + vols.volEnd[i] = volEnd; + } + vols.channel = channel; + return vols; +} + +ChannelVolumes VolumeTools::GetChannelVolumes(AudioChannel channel, float volStart, float volEnd) +{ + ChannelVolumes vols = {}; + if (!IsVolumeValid(volStart) || !IsVolumeValid(volEnd) || channel > CHANNEL_16 || channel < MONO) { + AUDIO_ERR_LOG("GetChannelVolumes failed with invalid vol:%{public}f %{public}f channel: %{public}d", volStart, + volEnd, channel); + return vols; + } + for (size_t i = 0; i < channel; i++) { + vols.volStart[i] = GetInt32Vol(volStart); + vols.volEnd[i] = GetInt32Vol(volEnd); + } + vols.channel = channel; + return vols; +} + +inline size_t GetByteSize(AudioSampleFormat format) +{ + size_t bitWidthSize = 0; + switch (format) { + case SAMPLE_U8: + bitWidthSize = 1; // size is 1 + break; + case SAMPLE_S16LE: + bitWidthSize = 2; // size is 2 + break; + case SAMPLE_S24LE: + bitWidthSize = 3; // size is 3 + break; + case SAMPLE_S32LE: + bitWidthSize = 4; // size is 4 + break; + case SAMPLE_F32LE: + bitWidthSize = 4; // size is 4 + break; + default: + bitWidthSize = 2; // default size is 2 + break; + } + return bitWidthSize; +} + +static inline uint32_t ReadInt24LE(const uint8_t *p) +{ + return ((uint32_t) p[ARRAY_INDEX_TWO] << SHIFT_SIXTEEN) | ((uint32_t) p[1] << SHIFT_EIGHT) | ((uint32_t) p[0]); +} + +static inline void WriteInt24LE(uint8_t *p, uint32_t u) +{ + p[ARRAY_INDEX_TWO] = (uint8_t) (u >> SHIFT_SIXTEEN); + p[1] = (uint8_t) (u >> SHIFT_EIGHT); + p[0] = (uint8_t) u; +} + +inline int32_t VolumeFlatten(int32_t vol) +{ + return vol < INT32_VOLUME_MIN ? 0 : (vol > INT32_VOLUME_MAX ? INT32_VOLUME_MAX : vol); +} + +inline void ProcessOneFrame(uint8_t *ptr, AudioSampleFormat format, int32_t vol) +{ + int64_t temp = 0; + int16_t *raw16 = nullptr; + int32_t *raw32 = nullptr; + float *rawFloat = nullptr; + switch (format) { + case SAMPLE_U8: + temp = *ptr - UINT8_SHIFT; + temp = (temp * vol) >> VOLUME_SHIFT; + temp = temp < INT8_MIN ? INT8_MIN : (temp > INT8_MAX ? INT8_MAX : temp); + *ptr = static_cast(temp + UINT8_SHIFT); + break; + case SAMPLE_S16LE: + raw16 = reinterpret_cast(ptr); + temp = (*raw16 * static_cast(vol)) >> VOLUME_SHIFT; + *raw16 = temp > INT16_MAX ? INT16_MAX : (temp < INT16_MIN ? INT16_MIN : temp); + break; + case SAMPLE_S24LE: + temp = static_cast(ReadInt24LE(ptr) << INT24_SHIFT) * static_cast(vol) >> VOLUME_SHIFT; + WriteInt24LE(ptr, (static_cast(temp) >> INT24_SHIFT)); + break; + case SAMPLE_S32LE: + raw32 = reinterpret_cast(ptr); + // int32_t * int16_t, max result is int48_t + temp = (*raw32 * static_cast(vol)) >> VOLUME_SHIFT; + *raw32 = temp > INT32_MAX ? INT32_MAX : (temp < INT32_MIN ? INT32_MIN : temp); + break; + case SAMPLE_F32LE: + rawFloat = reinterpret_cast(ptr); + *rawFloat = *rawFloat * (static_cast(vol) / INT32_VOLUME_MAX); + break; + default: + AUDIO_ERR_LOG("ProcessOneFrame with invalid format"); + break; + } +} + +// |---------frame1--------|---------frame2--------|---------frame3--------| +// |ch1-ch2-ch3-ch4-ch5-ch6|ch1-ch2-ch3-ch4-ch5-ch6|ch1-ch2-ch3-ch4-ch5-ch6| +int32_t VolumeTools::Process(const BufferDesc &buffer, AudioSampleFormat format, ChannelVolumes vols) +{ + // parms check + if (format > SAMPLE_F32LE || !IsVolumeValid(vols)) { + AUDIO_ERR_LOG("Process failed with invalid params"); + return ERR_INVALID_PARAM; + } + size_t byteSizePerData = GetByteSize(format); + size_t byteSizePerFrame = byteSizePerData * vols.channel; + if (buffer.buffer == nullptr || buffer.bufLength % byteSizePerFrame != 0) { + AUDIO_ERR_LOG("Process failed with invalid buffer, size is %{public}zu", buffer.bufLength); + return ERR_INVALID_PARAM; + } + + size_t frameSize = buffer.bufLength / byteSizePerFrame; + if (frameSize <= MIN_FRAME_SIZE) { + AUDIO_ERR_LOG("Process failed with invalid frameSize, size is %{public}zu", frameSize); + return ERR_INVALID_PARAM; + } + + float volStep[CHANNEL_MAX] = {}; + for (size_t channelIdx = 0; channelIdx < vols.channel; channelIdx++) { + if (vols.volEnd[channelIdx] == vols.volStart[channelIdx]) { + volStep[channelIdx] = 0.0; + } else { + volStep[channelIdx] = (static_cast(vols.volEnd[channelIdx] - vols.volStart[channelIdx])) / + (frameSize - MIN_FRAME_SIZE); + } + } + for (size_t frameIndex = 0; frameIndex < frameSize; frameIndex++) { + for (size_t channelIdx = 0; channelIdx < vols.channel; channelIdx++) { + int32_t vol = volStep[channelIdx] * frameIndex + vols.volStart[channelIdx]; + vol = VolumeFlatten(vol); + uint8_t *samplePtr = buffer.buffer + frameIndex * byteSizePerFrame + channelIdx * byteSizePerData; + ProcessOneFrame(samplePtr, format, vol); + } + } + + return SUCCESS; +} +} // namespace AudioStandard +} // namespace OHOS \ No newline at end of file diff --git a/services/audio_service/server/include/audio_service.h b/services/audio_service/server/include/audio_service.h index cdd4c3ea293c10f9ea03851b31e5b2e078ae9d4a..5a0d966e2f1754c5f1f849b4ef7ccd6537cd4607 100644 --- a/services/audio_service/server/include/audio_service.h +++ b/services/audio_service/server/include/audio_service.h @@ -25,6 +25,7 @@ #include "audio_process_in_server.h" #include "audio_endpoint.h" +#include "ipc_stream_in_server.h" namespace OHOS { namespace AudioStandard { @@ -32,6 +33,9 @@ class AudioService : public ProcessReleaseCallback { public: static AudioService *GetInstance(); ~AudioService(); + + sptr GetIpcStream(const AudioProcessConfig &config, int32_t &ret); + sptr GetAudioProcess(const AudioProcessConfig &config); // override for ProcessReleaseCallback, do release process work. int32_t OnProcessRelease(IAudioProcessStream *process) override; diff --git a/services/audio_service/server/include/capturer_in_server.h b/services/audio_service/server/include/capturer_in_server.h new file mode 100644 index 0000000000000000000000000000000000000000..f503c3781fec0ea55f9612fa7c669ee9bfd4a81e --- /dev/null +++ b/services/audio_service/server/include/capturer_in_server.h @@ -0,0 +1,89 @@ +/* + * 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 CAPTURER_IN_SERVER_H +#define CAPTURER_IN_SERVER_H + +#include +#include "i_capturer_stream.h" +#include "i_stream_listener.h" +#include "oh_audio_buffer.h" + +namespace OHOS { +namespace AudioStandard { +class CapturerListener { +public: + virtual void OnReadEvent() = 0; + virtual void ReceivedBuffer() = 0; +}; +class CapturerInServer : public IStatusCallback, public IReadCallback, + public std::enable_shared_from_this { +public: + // LYH waiting for review: add IStreamListener + CapturerInServer(AudioProcessConfig processConfig, std::weak_ptr streamListener); + virtual ~CapturerInServer(); + void OnStatusUpdate(IOperation operation) override; + int32_t OnReadData(size_t length) override; + + int32_t ResolveBuffer(std::shared_ptr &buffer); + int32_t GetSessionId(uint32_t &sessionId); + int32_t Start(); + int32_t Pause(); + int32_t Flush(); + int32_t Stop(); + int32_t Release(); + + int32_t GetAudioTime(uint64_t &framePos, uint64_t &timeStamp); + int32_t GetLatency(uint64_t &latency); + + void Init(); + void RegisterTestCallback(const std::weak_ptr &callback); + + int32_t ConfigServerBuffer(); + int32_t InitBufferStatus(); + int32_t UpdateReadIndex(); + BufferDesc DequeueBuffer(size_t length); + void ReadData(size_t length); + int32_t DrainAudioBuffer(); + int32_t ReadOneFrame(); + std::shared_ptr GetOHSharedBuffer(); + +private: + std::mutex statusLock_; + std::condition_variable statusCv_; + std::shared_ptr stream_ = nullptr; + uint32_t streamIndex_ = -1; + IOperation operation_ = OPERATION_INVALID; + IStatus status_ = I_STATUS_IDLE; + + // LYH waiting for review: IStreamListener + std::weak_ptr streamListener_; + std::weak_ptr testCallback_; + AudioProcessConfig processConfig_; + size_t totalSizeInFrame_ = 0; + size_t spanSizeInFrame_ = 0; + size_t byteSizePerFrame_ = 0; + size_t spanSizeInBytes_ = 0; + bool isBufferConfiged_ = false; + std::atomic isInited_ = false; + std::shared_ptr audioServerBuffer_ = nullptr; + int32_t needStart = 0; + int32_t underflowCount = 0; + bool resetTime_ = false; + uint64_t resetTimestamp_ = 0; +}; +} // namespace AudioStandard +} // namespace OHOS +#endif // CAPTURER_IN_SERVER_H diff --git a/services/audio_service/server/include/i_capturer_stream.h b/services/audio_service/server/include/i_capturer_stream.h new file mode 100644 index 0000000000000000000000000000000000000000..7e697993f8a8032bdbd0532357902ee05d2aef75 --- /dev/null +++ b/services/audio_service/server/include/i_capturer_stream.h @@ -0,0 +1,46 @@ +/* + * 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 I_CAPTURER_STREAM_H +#define I_CAPTURER_STREAM_H + +#include "i_stream.h" +#include "audio_info.h" +#include "audio_stream_info.h" + +namespace OHOS { +namespace AudioStandard { +class IReadCallback { +public: + virtual int32_t OnReadData(size_t length) = 0; +}; + +class ICapturerStream : public IStream { +public: + virtual ~ICapturerStream() = default; + virtual int32_t GetStreamFramesRead(uint64_t &framesRead) = 0; + virtual int32_t GetCurrentTimeStamp(uint64_t &timeStamp) = 0; + virtual int32_t GetLatency(uint64_t &latency) = 0; + + virtual void RegisterReadCallback(const std::weak_ptr &callback) = 0; + virtual int32_t GetMinimumBufferSize(size_t &minBufferSize) const = 0; + virtual void GetByteSizePerFrame(size_t &byteSizePerFrame) const = 0; + virtual void GetSpanSizePerFrame(size_t &spanSizeInFrame) const = 0; + virtual int32_t DropBuffer() = 0; + virtual void AbortCallback(int32_t abortTimes) = 0; +}; +} // namespace AudioStandard +} // namespace OHOS +#endif // I_CAPTURER_STREAM_H diff --git a/services/audio_service/server/include/i_renderer_stream.h b/services/audio_service/server/include/i_renderer_stream.h new file mode 100644 index 0000000000000000000000000000000000000000..5fa66e8d8adf187d136f5b696d8aa13eed9812d3 --- /dev/null +++ b/services/audio_service/server/include/i_renderer_stream.h @@ -0,0 +1,52 @@ +/* + * 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 I_RENDERER_STREAM_H +#define I_RENDERER_STREAM_H + +#include "i_stream.h" +#include "audio_info.h" +#include "audio_stream_info.h" + +namespace OHOS { +namespace AudioStandard { +class IWriteCallback { +public: + virtual int32_t OnWriteData(size_t length) = 0; +}; + +class IRendererStream : public IStream { +public: + virtual ~IRendererStream() = default; + virtual int32_t GetStreamFramesWritten(uint64_t &framesWritten) = 0; + virtual int32_t GetCurrentTimeStamp(uint64_t &timeStamp) = 0; + virtual int32_t GetLatency(uint64_t &latency) = 0; + virtual int32_t SetRate(int32_t rate) = 0; + virtual int32_t SetLowPowerVolume(float volume) = 0; + virtual int32_t GetLowPowerVolume(float &volume) = 0; + virtual int32_t SetAudioEffectMode(int32_t effectMode) = 0; + virtual int32_t GetAudioEffectMode(int32_t &effectMode) = 0; + virtual int32_t SetPrivacyType(int32_t privacyType) = 0; + virtual int32_t GetPrivacyType(int32_t &privacyType) = 0; + + virtual void RegisterWriteCallback(const std::weak_ptr &callback) = 0; + virtual int32_t GetMinimumBufferSize(size_t &minBufferSize) const = 0; + virtual void GetByteSizePerFrame(size_t &byteSizePerFrame) const = 0; + virtual void GetSpanSizePerFrame(size_t &spanSizeInFrame) const = 0; + virtual void AbortCallback(int32_t abortTimes) = 0; +}; +} // namespace AudioStandard +} // namespace OHOS +#endif // I_RENDERER_STREAM_H diff --git a/services/audio_service/server/include/i_stream.h b/services/audio_service/server/include/i_stream.h new file mode 100644 index 0000000000000000000000000000000000000000..d6912e47ba5628ae86896a74909e7747f52eb633 --- /dev/null +++ b/services/audio_service/server/include/i_stream.h @@ -0,0 +1,75 @@ +/* + * 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 I_STREAM_H +#define I_STREAM_H + +#include "audio_info.h" +#include "audio_stream_info.h" + +namespace OHOS { +namespace AudioStandard { +enum IOperation { + OPERATION_INVALID = -1, + OPERATION_STARTED, + OPERATION_PAUSED, + OPERATION_STOPPED, + OPERATION_FLUSHED, + OPERATION_DRAINED, + OPERATION_RELEASED, + OPERATION_UNDERRUN, + OPERATION_UNDERFLOW, +}; + +enum IStatus { + I_STATUS_INVALID = -1, + I_STATUS_IDLE, + I_STATUS_STARTING, + I_STATUS_STARTED, + I_STATUS_PAUSING, + I_STATUS_PAUSED, + I_STATUS_FLUSHING_WHEN_STARTED, + I_STATUS_FLUSHING_WHEN_PAUSED, + I_STATUS_FLUSHING_WHEN_STOPPED, + I_STATUS_DRAINING, + I_STATUS_DRAINED, + I_STATUS_STOPPING, + I_STATUS_STOPPED, + I_STATUS_RELEASING, + I_STATUS_RELEASED, +}; + +class IStatusCallback { +public: + virtual void OnStatusUpdate(IOperation operation) = 0; +}; + +class IStream { +public: + virtual void SetStreamIndex(uint32_t index) = 0; + virtual uint32_t GetStreamIndex() = 0; + virtual int32_t Start() = 0; + virtual int32_t Pause() = 0; + virtual int32_t Flush() = 0; + virtual int32_t Drain() = 0; + virtual int32_t Stop() = 0; + virtual int32_t Release() = 0; + virtual void RegisterStatusCallback(const std::weak_ptr &callback) = 0; + virtual BufferDesc DequeueBuffer(size_t length) = 0; + virtual int32_t EnqueueBuffer(const BufferDesc &bufferDesc) = 0; +}; +} // namespace AudioStandard +} // namespace OHOS +#endif // I_STREAM_H diff --git a/services/audio_service/server/include/i_stream_manager.h b/services/audio_service/server/include/i_stream_manager.h new file mode 100644 index 0000000000000000000000000000000000000000..92aa25f76d9e64026e7fe4303ae3e76091978bc6 --- /dev/null +++ b/services/audio_service/server/include/i_stream_manager.h @@ -0,0 +1,44 @@ +/* + * 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 I_STREAM_MANAGER_H +#define I_STREAM_MANAGER_H + +#include "i_renderer_stream.h" +#include "i_capturer_stream.h" + +namespace OHOS { +namespace AudioStandard { +enum ManagerType : int32_t { + PLAYBACK = 0, + RECORDER, +}; + +class IStreamManager { +public: + ~IStreamManager() = default; + + static IStreamManager &GetPlaybackManager(); + static IStreamManager &GetRecorderManager(); + + virtual int32_t CreateRender(AudioProcessConfig processConfig, std::shared_ptr &stream) = 0; + virtual int32_t ReleaseRender(uint32_t streamIndex_) = 0; + virtual int32_t CreateCapturer(AudioProcessConfig processConfig, std::shared_ptr &stream) = 0; + virtual int32_t ReleaseCapturer(uint32_t streamIndex_) = 0; + virtual int32_t GetInfo() = 0; +}; +} // namespace AudioStandard +} // namespace OHOS +#endif // I_STREAM_MANAGER_H diff --git a/services/audio_service/server/include/ipc_stream_in_server.h b/services/audio_service/server/include/ipc_stream_in_server.h new file mode 100644 index 0000000000000000000000000000000000000000..3fd99e10605696fd246f1fb935cd995a657d50c4 --- /dev/null +++ b/services/audio_service/server/include/ipc_stream_in_server.h @@ -0,0 +1,105 @@ +/* + * 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 IPC_STREAM_IN_SERVER_H +#define IPC_STREAM_IN_SERVER_H + +#include + +#include "ipc_stream_stub.h" +#include "audio_info.h" +#include "audio_process_config.h" +#include "renderer_in_server.h" +#include "capturer_in_server.h" + +namespace OHOS { +namespace AudioStandard { +// in plan extends IStatusCallback +class StreamListenerHolder : public IStreamListener { +public: + StreamListenerHolder(); + ~StreamListenerHolder(); + int32_t RegisterStreamListener(sptr listener); + + // override IStreamListener + int32_t OnOperationHandled(Operation operation, int64_t result) override; +private: + std::mutex listenerMutex_; + sptr streamListener_ = nullptr; +}; + +class IpcStreamInServer : public IpcStreamStub { +public: + static sptr Create(const AudioProcessConfig &config, int32_t &ret); + + IpcStreamInServer(const AudioProcessConfig &config, AudioMode mode); + ~IpcStreamInServer(); + + int32_t Config(); + + int32_t RegisterStreamListener(sptr object) override; + + int32_t ResolveBuffer(std::shared_ptr &buffer) override; + + int32_t UpdatePosition() override; + + int32_t GetAudioSessionID(uint32_t &sessionId) override; + + int32_t Start() override; + + int32_t Pause() override; + + int32_t Stop() override; + + int32_t Release() override; + + int32_t Flush() override; + + int32_t Drain() override; + + int32_t GetAudioTime(uint64_t &framePos, uint64_t &timeStamp) override; + + int32_t GetLatency(uint64_t &latency) override; + + int32_t SetRate(int32_t rate) override; // SetRenderRate + + int32_t GetRate(int32_t &rate) override; // SetRenderRate + + int32_t SetLowPowerVolume(float volume) override; // renderer only + + int32_t GetLowPowerVolume(float &volume) override; // renderer only + + int32_t SetAudioEffectMode(int32_t effectMode) override; // renderer only + + int32_t GetAudioEffectMode(int32_t &effectMode) override; // renderer only + + int32_t SetPrivacyType(int32_t privacyType) override; // renderer only + + int32_t GetPrivacyType(int32_t &privacyType) override; // renderer only + +private: + int32_t ConfigRenderer(); + int32_t ConfigCapturer(); + +private: + AudioProcessConfig config_; + std::shared_ptr streamListenerHolder_ = nullptr; + AudioMode mode_ = AUDIO_MODE_PLAYBACK; + std::shared_ptr rendererInServer_ = nullptr; + std::shared_ptr capturerInServer_ = nullptr; +}; +} // namespace AudioStandard +} // namespace OHOS +#endif // IPC_STREAM_IN_SERVER_H diff --git a/services/audio_service/server/include/ipc_stream_listener_proxy.h b/services/audio_service/server/include/ipc_stream_listener_proxy.h new file mode 100644 index 0000000000000000000000000000000000000000..0825b79e1c6d245a793db090f78f643cd519a567 --- /dev/null +++ b/services/audio_service/server/include/ipc_stream_listener_proxy.h @@ -0,0 +1,37 @@ +/* + * 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 IPC_STREAM_LISTENER_PROXY_H +#define IPC_STREAM_LISTENER_PROXY_H + +#include "message_parcel.h" + +#include "ipc_stream.h" + +namespace OHOS { +namespace AudioStandard { +class IpcStreamListenerProxy : public IRemoteProxy { +public: + explicit IpcStreamListenerProxy(const sptr &impl); + virtual ~IpcStreamListenerProxy(); + + int32_t OnOperationHandled(Operation operation, int64_t result) override; + +private: + static inline BrokerDelegator delegator_; +}; +} // namespace AudioStandard +} // namespace OHOS +#endif // IPC_STREAM_LISTENER_PROXY_H diff --git a/services/audio_service/server/include/ipc_stream_stub.h b/services/audio_service/server/include/ipc_stream_stub.h new file mode 100644 index 0000000000000000000000000000000000000000..3b18b910cd0e90b55c2a8e1bffeda001ab9b63df --- /dev/null +++ b/services/audio_service/server/include/ipc_stream_stub.h @@ -0,0 +1,80 @@ +/* + * 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 IPC_STREAM_STUB_H +#define IPC_STREAM_STUB_H + +#include "ipc_stream.h" + +#include "message_parcel.h" + +namespace OHOS { +namespace AudioStandard { +class IpcStreamStub : public IRemoteStub { +public: + virtual ~IpcStreamStub() = default; + int OnRemoteRequest(uint32_t code, MessageParcel &data, MessageParcel &reply, MessageOption &option) override; +private: + static bool CheckInterfaceToken(MessageParcel &data); + + int32_t HandleRegisterStreamListener(MessageParcel &data, MessageParcel &reply); + int32_t HandleResolveBuffer(MessageParcel &data, MessageParcel &reply); + int32_t HandleUpdatePosition(MessageParcel &data, MessageParcel &reply); + int32_t HandleGetAudioSessionID(MessageParcel &data, MessageParcel &reply); + int32_t HandleStart(MessageParcel &data, MessageParcel &reply); + int32_t HandlePause(MessageParcel &data, MessageParcel &reply); + int32_t HandleStop(MessageParcel &data, MessageParcel &reply); + int32_t HandleRelease(MessageParcel &data, MessageParcel &reply); + int32_t HandleFlush(MessageParcel &data, MessageParcel &reply); + int32_t HandleDrain(MessageParcel &data, MessageParcel &reply); + int32_t HandleGetAudioTime(MessageParcel &data, MessageParcel &reply); + int32_t HandleGetLatency(MessageParcel &data, MessageParcel &reply); + int32_t HandleSetRate(MessageParcel &data, MessageParcel &reply); + int32_t HandleGetRate(MessageParcel &data, MessageParcel &reply); + int32_t HandleSetLowPowerVolume(MessageParcel &data, MessageParcel &reply); + int32_t HandleGetLowPowerVolume(MessageParcel &data, MessageParcel &reply); + int32_t HandleSetAudioEffectMode(MessageParcel &data, MessageParcel &reply); + int32_t HandleGetAudioEffectMode(MessageParcel &data, MessageParcel &reply); + int32_t HandleSetPrivacyType(MessageParcel &data, MessageParcel &reply); + int32_t HandleGetPrivacyType(MessageParcel &data, MessageParcel &reply); + + using HandlerFunc = int32_t(IpcStreamStub::*)(MessageParcel &data, MessageParcel &reply); + // Using the same order in IpcStreamMsg::Code when add func! + static inline HandlerFunc funcList_[IpcStreamMsg::IPC_STREAM_MAX_MSG] = { + &IpcStreamStub::HandleRegisterStreamListener, + &IpcStreamStub::HandleResolveBuffer, + &IpcStreamStub::HandleUpdatePosition, + &IpcStreamStub::HandleGetAudioSessionID, + &IpcStreamStub::HandleStart, + &IpcStreamStub::HandlePause, + &IpcStreamStub::HandleStop, + &IpcStreamStub::HandleRelease, + &IpcStreamStub::HandleFlush, + &IpcStreamStub::HandleDrain, + &IpcStreamStub::HandleGetAudioTime, + &IpcStreamStub::HandleGetLatency, + &IpcStreamStub::HandleSetRate, + &IpcStreamStub::HandleGetRate, + &IpcStreamStub::HandleSetLowPowerVolume, + &IpcStreamStub::HandleGetLowPowerVolume, + &IpcStreamStub::HandleSetAudioEffectMode, + &IpcStreamStub::HandleGetAudioEffectMode, + &IpcStreamStub::HandleSetPrivacyType, + &IpcStreamStub::HandleGetPrivacyType, + }; +}; +} // namespace AudioStandard +} // namespace OHOS +#endif // IPC_STREAM_STUB_H diff --git a/services/audio_service/server/include/pa_adapter_manager.h b/services/audio_service/server/include/pa_adapter_manager.h new file mode 100644 index 0000000000000000000000000000000000000000..1a1cc4024e2fce67a2dedcd43fb3c56bc5ee6eb2 --- /dev/null +++ b/services/audio_service/server/include/pa_adapter_manager.h @@ -0,0 +1,114 @@ +/* + * 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 PA_ADAPTER_MANAGER_H +#define PA_ADAPTER_MANAGER_H + +#include +#include +#include +#include +#include "audio_timer.h" +#include "i_stream_manager.h" + +namespace OHOS { +namespace AudioStandard { + +static std::map defaultChCountToLayoutMap = { + {1, CH_LAYOUT_MONO}, {2, CH_LAYOUT_STEREO}, {3, CH_LAYOUT_SURROUND}, + {4, CH_LAYOUT_2POINT0POINT2}, {5, CH_LAYOUT_5POINT0_BACK}, {6, CH_LAYOUT_5POINT1_BACK}, + {7, CH_LAYOUT_6POINT1_BACK}, {8, CH_LAYOUT_5POINT1POINT2}, {10, CH_LAYOUT_7POINT1POINT2}, + {12, CH_LAYOUT_7POINT1POINT4}, {14, CH_LAYOUT_9POINT1POINT4}, {16, CH_LAYOUT_9POINT1POINT6} +}; + +static std::map chSetToPaPositionMap = { + {FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_LEFT}, {FRONT_RIGHT, PA_CHANNEL_POSITION_FRONT_RIGHT}, + {FRONT_CENTER, PA_CHANNEL_POSITION_FRONT_CENTER}, {LOW_FREQUENCY, PA_CHANNEL_POSITION_LFE}, + {SIDE_LEFT, PA_CHANNEL_POSITION_SIDE_LEFT}, {SIDE_RIGHT, PA_CHANNEL_POSITION_SIDE_RIGHT}, + {BACK_LEFT, PA_CHANNEL_POSITION_REAR_LEFT}, {BACK_RIGHT, PA_CHANNEL_POSITION_REAR_RIGHT}, + {FRONT_LEFT_OF_CENTER, PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER}, + {FRONT_RIGHT_OF_CENTER, PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER}, + {BACK_CENTER, PA_CHANNEL_POSITION_REAR_CENTER}, {TOP_CENTER, PA_CHANNEL_POSITION_TOP_CENTER}, + {TOP_FRONT_LEFT, PA_CHANNEL_POSITION_TOP_FRONT_LEFT}, {TOP_FRONT_CENTER, PA_CHANNEL_POSITION_TOP_FRONT_CENTER}, + {TOP_FRONT_RIGHT, PA_CHANNEL_POSITION_TOP_FRONT_RIGHT}, {TOP_BACK_LEFT, PA_CHANNEL_POSITION_TOP_REAR_LEFT}, + {TOP_BACK_CENTER, PA_CHANNEL_POSITION_TOP_REAR_CENTER}, {TOP_BACK_RIGHT, PA_CHANNEL_POSITION_TOP_REAR_RIGHT}, + /** Channel layout positions below do not have precise mapped pulseaudio positions */ + {STEREO_LEFT, PA_CHANNEL_POSITION_FRONT_LEFT}, {STEREO_RIGHT, PA_CHANNEL_POSITION_FRONT_RIGHT}, + {WIDE_LEFT, PA_CHANNEL_POSITION_FRONT_LEFT}, {WIDE_RIGHT, PA_CHANNEL_POSITION_FRONT_RIGHT}, + {SURROUND_DIRECT_LEFT, PA_CHANNEL_POSITION_SIDE_LEFT}, {SURROUND_DIRECT_RIGHT, PA_CHANNEL_POSITION_SIDE_LEFT}, + {BOTTOM_FRONT_CENTER, PA_CHANNEL_POSITION_FRONT_CENTER}, + {BOTTOM_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_LEFT}, {BOTTOM_FRONT_RIGHT, PA_CHANNEL_POSITION_FRONT_RIGHT}, + {TOP_SIDE_LEFT, PA_CHANNEL_POSITION_TOP_REAR_LEFT}, {TOP_SIDE_RIGHT, PA_CHANNEL_POSITION_TOP_REAR_RIGHT}, + {LOW_FREQUENCY_2, PA_CHANNEL_POSITION_LFE}, +}; + +class PaAdapterManager : public IStreamManager { +public: + PaAdapterManager(ManagerType type); + + int32_t CreateRender(AudioProcessConfig processConfig, std::shared_ptr &stream) override; + int32_t ReleaseRender(uint32_t streamIndex_) override; + int32_t CreateCapturer(AudioProcessConfig processConfig, std::shared_ptr &stream) override; + int32_t ReleaseCapturer(uint32_t streamIndex_) override; + uint32_t ConvertChLayoutToPaChMap(const uint64_t &channelLayout, pa_channel_map &paMap); + const std::string GetEffectSceneName(AudioStreamType audioType); + + int32_t GetInfo() override; + +private: + // audio channel index + static const uint8_t CHANNEL1_IDX = 0; + static const uint8_t CHANNEL2_IDX = 1; + static const uint8_t CHANNEL3_IDX = 2; + static const uint8_t CHANNEL4_IDX = 3; + static const uint8_t CHANNEL5_IDX = 4; + static const uint8_t CHANNEL6_IDX = 5; + static const uint8_t CHANNEL7_IDX = 6; + static const uint8_t CHANNEL8_IDX = 7; + + int32_t ResetPaContext(); + int32_t InitPaContext(); + int32_t HandleMainLoopStart(); + pa_stream *InitPaStream(AudioProcessConfig processConfig, uint32_t sessionId); + int32_t SetPaProplist(pa_proplist *propList, pa_channel_map &map, AudioProcessConfig &processConfig, + const std::string &streamName, uint32_t sessionId); + std::shared_ptr CreateRendererStream(AudioProcessConfig processConfig, pa_stream *paStream); + std::shared_ptr CreateCapturerStream(AudioProcessConfig processConfig, pa_stream *paStream); + int32_t ConnectStreamToPA(pa_stream *paStream, pa_sample_spec sampleSpec); + int32_t ConnectRendererStreamToPA(pa_stream *paStream, pa_sample_spec sampleSpec); + int32_t ConnectCapturerStreamToPA(pa_stream *paStream, pa_sample_spec sampleSpec); + + // Callbacks to be implemented + static void PAStreamStateCb(pa_stream *stream, void *userdata); + static void PAContextStateCb(pa_context *context, void *userdata); + + static void PAStreamUpdateStreamIndexSuccessCb(pa_stream *stream, int32_t success, void *userdata); + + const std::string GetStreamName(AudioStreamType audioType); + pa_sample_spec ConvertToPAAudioParams(AudioProcessConfig processConfig); + + pa_threaded_mainloop *mainLoop_; + pa_mainloop_api *api_; + pa_context *context_; + std::mutex listLock_; + std::map> rendererStreamMap_; + std::map> capturerStreamMap_; + bool isContextConnected_; + bool isMainLoopStarted_; + ManagerType managerType_ = PLAYBACK; +}; +} // namespace AudioStandard +} // namespace OHOS +#endif // PA_ADAPTER_MANAGER_H diff --git a/services/audio_service/server/include/pa_capturer_stream_impl.h b/services/audio_service/server/include/pa_capturer_stream_impl.h new file mode 100644 index 0000000000000000000000000000000000000000..81c507cb67e3bc16709e2a028a3b41660c840e7b --- /dev/null +++ b/services/audio_service/server/include/pa_capturer_stream_impl.h @@ -0,0 +1,87 @@ +/* + * 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 PA_CAPTURER_STREAM_IMPL_H +#define PA_CAPTURER_STREAM_IMPL_H + +#include +#include "i_capturer_stream.h" + +namespace OHOS { +namespace AudioStandard { +class PaCapturerStreamImpl : public ICapturerStream { +public: + PaCapturerStreamImpl(pa_stream *paStream, AudioProcessConfig processConfig, pa_threaded_mainloop *mainloop); + void InitParams(); + int32_t Start() override; + int32_t Pause() override; + int32_t Flush() override; + int32_t Drain() override { return 0; }; + int32_t Stop() override; + int32_t Release() override; + int32_t CorkStream(); + int32_t GetStreamFramesRead(uint64_t &framesRead) override; + int32_t GetCurrentTimeStamp(uint64_t &timeStamp) override; + int32_t GetLatency(uint64_t &latency) override; + + void RegisterStatusCallback(const std::weak_ptr &callback) override; + void RegisterReadCallback(const std::weak_ptr &callback) override; + BufferDesc DequeueBuffer(size_t length) override; + int32_t EnqueueBuffer(const BufferDesc &bufferDesc) override; + int32_t GetMinimumBufferSize(size_t &minBufferSize) const override; + void GetByteSizePerFrame(size_t &byteSizePerFrame) const override; + void GetSpanSizePerFrame(size_t &spanSizeInFrame) const override; + void SetStreamIndex(uint32_t index) override; + uint32_t GetStreamIndex() override; + int32_t DropBuffer() override; + void AbortCallback(int32_t abortTimes) override; + +private: + static void PAStreamReadCb(pa_stream *stream, size_t length, void *userdata); + static void PAStreamMovedCb(pa_stream *stream, void *userdata); + static void PAStreamUnderFlowCb(pa_stream *stream, void *userdata); + static void PAStreamSetStartedCb(pa_stream *stream, void *userdata); + static void PAStreamStartSuccessCb(pa_stream *stream, int32_t success, void *userdata); + static void PAStreamPauseSuccessCb(pa_stream *stream, int32_t success, void *userdata); + static void PAStreamFlushSuccessCb(pa_stream *stream, int32_t success, void *userdata); + static void PAStreamStopSuccessCb(pa_stream *stream, int32_t success, void *userdata); + pa_stream_success_cb_t PAStreamCorkSuccessCb; + + uint32_t streamIndex_ = static_cast(-1); // invalid index + + pa_stream *paStream_ = nullptr; + AudioProcessConfig processConfig_; + std::weak_ptr statusCallback_; + std::weak_ptr readCallback_; + int32_t streamCmdStatus_; + int32_t streamFlushStatus_; + State state_; + uint32_t underFlowCount_; + pa_threaded_mainloop *mainloop_; + + size_t byteSizePerFrame_ = 0; + size_t spanSizeInFrame_ = 0; + size_t minBufferSize_ = 0; + + size_t totalBytesRead_ = 0; + + FILE *capturerServerDumpFile_ = nullptr; + + // Only for debug + int32_t abortFlag_ = 0; +}; +} // namespace AudioStandard +} // namespace OHOS +#endif // PA_CAPTURER_STREAM_IMPL_H diff --git a/services/audio_service/server/include/pa_renderer_stream_impl.h b/services/audio_service/server/include/pa_renderer_stream_impl.h new file mode 100644 index 0000000000000000000000000000000000000000..d361ad7bb8bd972c1ee82616402a88282ab68321 --- /dev/null +++ b/services/audio_service/server/include/pa_renderer_stream_impl.h @@ -0,0 +1,107 @@ +/* + * 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 PA_RENDERER_STREAM_IMPL_H +#define PA_RENDERER_STREAM_IMPL_H + +#include +#include "i_renderer_stream.h" + +namespace OHOS { +namespace AudioStandard { +class PaRendererStreamImpl : public IRendererStream { +public: + PaRendererStreamImpl(pa_stream *paStream, AudioProcessConfig processConfig, pa_threaded_mainloop *mainloop); + void InitParams(); + int32_t Start() override; + int32_t Pause() override; + int32_t Flush() override; + int32_t Drain() override; + int32_t Stop() override; + int32_t Release() override; + int32_t CorkStream(); + int32_t GetStreamFramesWritten(uint64_t &framesWritten) override; + int32_t GetCurrentTimeStamp(uint64_t &timeStamp) override; + int32_t GetLatency(uint64_t &latency) override; + int32_t SetRate(int32_t rate) override; + int32_t SetLowPowerVolume(float volume) override; + int32_t GetLowPowerVolume(float &powerVolume) override; + int32_t SetAudioEffectMode(int32_t effectMode) override; + int32_t GetAudioEffectMode(int32_t &effectMode) override; + int32_t SetPrivacyType(int32_t privacyType) override; + int32_t GetPrivacyType(int32_t &privacyType) override; + + void RegisterStatusCallback(const std::weak_ptr &callback) override; + void RegisterWriteCallback(const std::weak_ptr &callback) override; + BufferDesc DequeueBuffer(size_t length) override; + int32_t EnqueueBuffer(const BufferDesc &bufferDesc) override; + int32_t GetMinimumBufferSize(size_t &minBufferSize) const override; + void GetByteSizePerFrame(size_t &byteSizePerFrame) const override; + void GetSpanSizePerFrame(size_t &spanSizeInFrame) const override; + void SetStreamIndex(uint32_t index) override; + uint32_t GetStreamIndex() override; + void AbortCallback(int32_t abortTimes) override; + +private: + static void PAStreamWriteCb(pa_stream *stream, size_t length, void *userdata); + static void PAStreamMovedCb(pa_stream *stream, void *userdata); + static void PAStreamUnderFlowCb(pa_stream *stream, void *userdata); + static void PAStreamSetStartedCb(pa_stream *stream, void *userdata); + static void PAStreamStartSuccessCb(pa_stream *stream, int32_t success, void *userdata); + static void PAStreamPauseSuccessCb(pa_stream *stream, int32_t success, void *userdata); + static void PAStreamFlushSuccessCb(pa_stream *stream, int32_t success, void *userdata); + static void PAStreamDrainSuccessCb(pa_stream *stream, int32_t success, void *userdata); + static void PAStreamDrainInStopCb(pa_stream *stream, int32_t success, void *userdata); + static void PAStreamAsyncStopSuccessCb(pa_stream *stream, int32_t success, void *userdata); + + const std::string GetEffectModeName(int32_t effectMode); + const std::string GetEffectSceneName(AudioStreamType audioType); + + uint32_t streamIndex_ = static_cast(-1); // invalid index + + pa_stream_success_cb_t PAStreamCorkSuccessCb; + pa_stream *paStream_ = nullptr; + AudioProcessConfig processConfig_; + std::weak_ptr statusCallback_; + std::weak_ptr writeCallback_; + int32_t streamCmdStatus_; + int32_t streamDrainStatus_; + int32_t streamFlushStatus_; + State state_; + uint32_t underFlowCount_; + bool isDrain_ = false; + pa_threaded_mainloop *mainloop_; + + size_t byteSizePerFrame_ = 0; + size_t spanSizeInFrame_ = 0; + size_t minBufferSize_ = 0; + + size_t totalBytesWritten_ = 0; + uint32_t sinkLatencyInMsec_ {0}; + int32_t renderRate_; + int32_t effectMode_ = -1; + std::string effectSceneName_ = "SCENE_MUSIC"; + int32_t privacyType_ = 0; + + float powerVolumeFactor_ = 1.0f; + + static constexpr float MAX_STREAM_VOLUME_LEVEL = 1.0f; + static constexpr float MIN_STREAM_VOLUME_LEVEL = 0.0f; + // Only for debug + int32_t abortFlag_ = 0; +}; +} // namespace AudioStandard +} // namespace OHOS +#endif // PA_RENDERER_STREAM_IMPL_H diff --git a/services/audio_service/server/include/renderer_in_server.h b/services/audio_service/server/include/renderer_in_server.h new file mode 100644 index 0000000000000000000000000000000000000000..8f230d5636407e5e680e217ab44472a68874a67f --- /dev/null +++ b/services/audio_service/server/include/renderer_in_server.h @@ -0,0 +1,92 @@ +/* + * 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 RENDERER_IN_SERVER_H +#define RENDERER_IN_SERVER_H + +#include +#include "i_renderer_stream.h" +#include "i_stream_listener.h" +#include "oh_audio_buffer.h" + +namespace OHOS { +namespace AudioStandard { +class RendererInServer : public IStatusCallback, public IWriteCallback, + public std::enable_shared_from_this { +public: + // LYH waiting for review: add IStreamListener + RendererInServer(AudioProcessConfig processConfig, std::weak_ptr streamListener); + virtual ~RendererInServer(); + void OnStatusUpdate(IOperation operation) override; + void HandleOperationFlushed(); + int32_t OnWriteData(size_t length) override; + + int32_t ResolveBuffer(std::shared_ptr &buffer); + int32_t GetSessionId(uint32_t &sessionId); + int32_t Start(); + int32_t Pause(); + int32_t Flush(); + int32_t Drain(); + int32_t Stop(); + int32_t Release(); + + int32_t GetAudioTime(uint64_t &framePos, uint64_t &timeStamp); + int32_t GetLatency(uint64_t &latency); + int32_t SetRate(int32_t rate); + int32_t SetLowPowerVolume(float volume); + int32_t GetLowPowerVolume(float &volume); + int32_t SetAudioEffectMode(int32_t effectMode); + int32_t GetAudioEffectMode(int32_t &effectMode); + int32_t SetPrivacyType(int32_t privacyType); + int32_t GetPrivacyType(int32_t &privacyType); + + void Init(); + int32_t ConfigServerBuffer(); + int32_t InitBufferStatus(); + int32_t UpdateWriteIndex(); + BufferDesc DequeueBuffer(size_t length); + void WriteData(); + void WriteEmptyData(); + int32_t DrainAudioBuffer(); + int32_t GetInfo(); + int32_t WriteOneFrame(); + std::shared_ptr GetOHSharedBuffer(); + +private: + std::mutex statusLock_; + std::condition_variable statusCv_; + std::shared_ptr stream_ = nullptr; + uint32_t streamIndex_ = -1; + IOperation operation_ = OPERATION_INVALID; + IStatus status_ = I_STATUS_IDLE; + + // LYH waiting for review + std::weak_ptr streamListener_; + AudioProcessConfig processConfig_; + size_t totalSizeInFrame_ = 0; + size_t spanSizeInFrame_ = 0; + size_t byteSizePerFrame_ = 0; + bool isBufferConfiged_ = false; + std::atomic isInited_ = false; + std::shared_ptr audioServerBuffer_ = nullptr; + int32_t needStart = 0; + bool afterDrain = false; + std::mutex updateIndexLock_; + bool resetTime_ = false; + uint64_t resetTimestamp_ = 0; +}; +} // namespace AudioStandard +} // namespace OHOS +#endif // RENDERER_IN_SERVER_H diff --git a/services/audio_service/server/src/audio_process_in_server.cpp b/services/audio_service/server/src/audio_process_in_server.cpp index d55279557770748e5531fabf948e763d1df06fa2..5634e430b94a3cf730ee855348c84d2e82b639dc 100644 --- a/services/audio_service/server/src/audio_process_in_server.cpp +++ b/services/audio_service/server/src/audio_process_in_server.cpp @@ -47,11 +47,15 @@ AudioProcessInServer::~AudioProcessInServer() int32_t AudioProcessInServer::ResolveBuffer(std::shared_ptr &buffer) { + AUDIO_INFO_LOG("ResolveBuffer start"); if (!isBufferConfiged_) { AUDIO_ERR_LOG("ResolveBuffer failed, buffer is not configed."); return ERR_ILLEGAL_STATE; } + if (processBuffer_ == nullptr) { + AUDIO_ERR_LOG("ResolveBuffer failed, buffer is nullptr."); + } buffer = processBuffer_; CHECK_AND_RETURN_RET_LOG(buffer != nullptr, ERR_ILLEGAL_STATE, "ResolveBuffer failed, processBuffer_ is null."); diff --git a/services/audio_service/server/src/audio_process_stub.cpp b/services/audio_service/server/src/audio_process_stub.cpp index 9811d42ed8bf9f56bd653a726e09ec481555c264..63f868d09167cd7b8f9598d426edac5848db1a57 100644 --- a/services/audio_service/server/src/audio_process_stub.cpp +++ b/services/audio_service/server/src/audio_process_stub.cpp @@ -69,6 +69,7 @@ int AudioProcessStub::OnRemoteRequest(uint32_t code, MessageParcel &data, Messag int32_t AudioProcessStub::HandleResolveBuffer(MessageParcel &data, MessageParcel &reply) { + AUDIO_INFO_LOG("HandleResolveBuffer"); (void)data; std::shared_ptr buffer; int32_t ret = ResolveBuffer(buffer); diff --git a/services/audio_service/server/src/audio_server.cpp b/services/audio_service/server/src/audio_server.cpp index 8854900e0365515df149509273fc956ce63354bf..d70bbf978580c5e9dd473081526e50dac95b9906 100644 --- a/services/audio_service/server/src/audio_server.cpp +++ b/services/audio_service/server/src/audio_server.cpp @@ -628,6 +628,16 @@ sptr AudioServer::CreateAudioProcess(const AudioProcessConfig &co return nullptr; } + if ((resetConfig.audioMode == AUDIO_MODE_PLAYBACK && resetConfig.rendererInfo.rendererFlags == 0) || + (resetConfig.audioMode == AUDIO_MODE_RECORD && resetConfig.capturerInfo.capturerFlags == 0)) { + AUDIO_INFO_LOG("Create normal ipc stream."); + int32_t ret = 0; + sptr ipcStream = AudioService::GetInstance()->GetIpcStream(resetConfig, ret); + CHECK_AND_RETURN_RET_LOG(ipcStream != nullptr, nullptr, "GetIpcStream failed."); + sptr remoteObject= ipcStream->AsObject(); + return remoteObject; + } + sptr process = AudioService::GetInstance()->GetAudioProcess(resetConfig); CHECK_AND_RETURN_RET_LOG(process != nullptr, nullptr, "GetAudioProcess failed."); sptr remoteObject= process->AsObject(); diff --git a/services/audio_service/server/src/audio_service.cpp b/services/audio_service/server/src/audio_service.cpp index b69024b69c67b74dbe67b2c422a6014e8f535a74..0655ee560b39f4f35b608cd76482b189e31f47f3 100644 --- a/services/audio_service/server/src/audio_service.cpp +++ b/services/audio_service/server/src/audio_service.cpp @@ -21,6 +21,7 @@ #include "audio_log.h" #include "remote_audio_renderer_sink.h" #include "policy_handler.h" +#include "ipc_stream_in_server.h" namespace OHOS { namespace AudioStandard { @@ -84,6 +85,14 @@ int32_t AudioService::OnProcessRelease(IAudioProcessStream *process) return SUCCESS; } +sptr AudioService::GetIpcStream(const AudioProcessConfig &config, int32_t &ret) +{ + // in plan: GetDeviceInfoForProcess(config) and stream limit check + sptr ipcStreamInServer = IpcStreamInServer::Create(config, ret); + + return ipcStreamInServer; +} + sptr AudioService::GetAudioProcess(const AudioProcessConfig &config) { AUDIO_INFO_LOG("GetAudioProcess dump %{public}s", ProcessConfig::DumpProcessConfig(config).c_str()); diff --git a/services/audio_service/server/src/capturer_in_server.cpp b/services/audio_service/server/src/capturer_in_server.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2e71de7e5da05e14c841625dc58b65049387a0df --- /dev/null +++ b/services/audio_service/server/src/capturer_in_server.cpp @@ -0,0 +1,402 @@ +/* + * 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 "capturer_in_server.h" +#include +#include "securec.h" +#include "audio_errors.h" +#include "audio_log.h" +#include "i_stream_manager.h" + +namespace OHOS { +namespace AudioStandard { +namespace { + static constexpr int32_t VOLUME_SHIFT_NUMBER = 16; // 1 >> 16 = 65536, max volume + static const size_t CAPTURER_BUFFER_MAX_NUM = 4; +} + +CapturerInServer::CapturerInServer(AudioProcessConfig processConfig, std::weak_ptr streamListener) +{ + processConfig_ = processConfig; + streamListener_ = streamListener; // LYH waiting for review + int32_t ret = IStreamManager::GetRecorderManager().CreateCapturer(processConfig, stream_); + AUDIO_INFO_LOG("Construct capturerInServer result: %{public}d", ret); + streamIndex_ = stream_->GetStreamIndex(); + ConfigServerBuffer(); +} + +CapturerInServer::~CapturerInServer() +{ + if (status_ != I_STATUS_RELEASED && status_ != I_STATUS_IDLE) { + Release(); + } +} + +int32_t CapturerInServer::ConfigServerBuffer() +{ + if (audioServerBuffer_ != nullptr) { + AUDIO_INFO_LOG("ConfigProcessBuffer: process buffer already configed!"); + return SUCCESS; + } + + stream_->GetSpanSizePerFrame(spanSizeInFrame_); + totalSizeInFrame_ = spanSizeInFrame_ * CAPTURER_BUFFER_MAX_NUM; // 4 frames + stream_->GetByteSizePerFrame(byteSizePerFrame_); + spanSizeInBytes_ = byteSizePerFrame_ * spanSizeInFrame_; + AUDIO_INFO_LOG("ConfigProcessBuffer: totalSizeInFrame_: %{public}zu, spanSizeInFrame_: %{public}zu," + "byteSizePerFrame_: %{public}zu, spanSizeInBytes_ %{public}zu", totalSizeInFrame_, spanSizeInFrame_, + byteSizePerFrame_, spanSizeInBytes_); + if (totalSizeInFrame_ == 0 || spanSizeInFrame_ == 0 || totalSizeInFrame_ % spanSizeInFrame_ != 0) { + AUDIO_ERR_LOG("ConfigProcessBuffer: ERR_INVALID_PARAM"); + return ERR_INVALID_PARAM; + } + + // create OHAudioBuffer in server + audioServerBuffer_ = OHAudioBuffer::CreateFromLocal(totalSizeInFrame_, spanSizeInFrame_, byteSizePerFrame_); + CHECK_AND_RETURN_RET_LOG(audioServerBuffer_ != nullptr, ERR_OPERATION_FAILED, "Create oh audio buffer failed"); + + // we need to clear data buffer to avoid dirty data. + memset_s(audioServerBuffer_->GetDataBase(), audioServerBuffer_->GetDataSize(), 0, + audioServerBuffer_->GetDataSize()); + int32_t ret = InitBufferStatus(); + AUDIO_DEBUG_LOG("Clear data buffer, ret:%{public}d", ret); + isBufferConfiged_ = true; + isInited_ = true; + return SUCCESS; +} + +int32_t CapturerInServer::InitBufferStatus() +{ + if (audioServerBuffer_ == nullptr) { + AUDIO_ERR_LOG("InitBufferStatus failed, null buffer."); + return ERR_ILLEGAL_STATE; + } + + uint32_t spanCount = audioServerBuffer_->GetSpanCount(); + AUDIO_INFO_LOG("InitBufferStatus: spanCount %{public}u", spanCount); + for (uint32_t i = 0; i < spanCount; i++) { + SpanInfo *spanInfo = audioServerBuffer_->GetSpanInfoByIndex(i); + if (spanInfo == nullptr) { + AUDIO_ERR_LOG("InitBufferStatus failed, null spaninfo"); + return ERR_ILLEGAL_STATE; + } + spanInfo->spanStatus = SPAN_READ_DONE; + spanInfo->offsetInFrame = 0; + + spanInfo->readStartTime = 0; + spanInfo->readDoneTime = 0; + + spanInfo->readStartTime = 0; + spanInfo->readDoneTime = 0; + + spanInfo->volumeStart = 1 << VOLUME_SHIFT_NUMBER; // 65536 for initialize + spanInfo->volumeEnd = 1 << VOLUME_SHIFT_NUMBER; // 65536 for initialize + spanInfo->isMute = false; + } + return SUCCESS; +} + +void CapturerInServer::Init() +{ + AUDIO_INFO_LOG("Init, register status and read callback"); + CHECK_AND_RETURN_LOG(stream_ != nullptr, "Capturer stream is nullptr"); + stream_->RegisterStatusCallback(shared_from_this()); + stream_->RegisterReadCallback(shared_from_this()); +} + +void CapturerInServer::OnStatusUpdate(IOperation operation) +{ + AUDIO_INFO_LOG("CapturerInServer::OnStatusUpdate operation: %{public}d", operation); + operation_ = operation; + std::lock_guard lock(statusLock_); + if (status_ == I_STATUS_RELEASED) { + AUDIO_WARNING_LOG("Stream already released"); + return; + } + std::shared_ptr stateListener = streamListener_.lock(); + CHECK_AND_RETURN_LOG(stateListener != nullptr, "IStreamListener is nullptr"); + // std::shared_ptr callback = testCallback_.lock(); + switch (operation) { + case OPERATION_UNDERFLOW: + underflowCount += 1; + AUDIO_INFO_LOG("Underflow!! underflow count %{public}d", underflowCount); + stateListener->OnOperationHandled(BUFFER_OVERFLOW, underflowCount); + break; + case OPERATION_STARTED: + status_ = I_STATUS_STARTED; + stateListener->OnOperationHandled(START_STREAM, 0); + break; + case OPERATION_PAUSED: + status_ = I_STATUS_PAUSED; + stateListener->OnOperationHandled(PAUSE_STREAM, 0); + break; + case OPERATION_STOPPED: + status_ = I_STATUS_STOPPED; + stateListener->OnOperationHandled(STOP_STREAM, 0); + break; + case OPERATION_FLUSHED: + if (status_ == I_STATUS_FLUSHING_WHEN_STARTED) { + status_ = I_STATUS_STARTED; + } else if (status_ == I_STATUS_FLUSHING_WHEN_PAUSED) { + status_ = I_STATUS_PAUSED; + } else if (status_ == I_STATUS_FLUSHING_WHEN_STOPPED) { + status_ = I_STATUS_STOPPED; + } else { + AUDIO_WARNING_LOG("Invalid status before flusing"); + } + stateListener->OnOperationHandled(FLUSH_STREAM, 0); + break; + default: + AUDIO_INFO_LOG("Invalid operation %{public}u", operation); + status_ = I_STATUS_INVALID; + } +} + +BufferDesc CapturerInServer::DequeueBuffer(size_t length) +{ + return stream_->DequeueBuffer(length); +} + +void CapturerInServer::ReadData(size_t length) +{ + if (length < spanSizeInBytes_) { + AUDIO_WARNING_LOG("Length %{public}zu is less than spanSizeInBytes %{public}zu", length, spanSizeInBytes_); + return; + } + std::shared_ptr stateListener = streamListener_.lock(); + CHECK_AND_RETURN_LOG(stateListener != nullptr, "IStreamListener is nullptr"); + + uint64_t currentReadFrame = audioServerBuffer_->GetCurReadFrame(); + uint64_t currentWriteFrame = audioServerBuffer_->GetCurWriteFrame(); + AUDIO_INFO_LOG("Current write frame: %{public}" PRIu64 ", read frame: %{public}" PRIu64 "," + "avaliable frame:%{public}d, spanSizeInFrame:%{public}zu", currentWriteFrame, currentReadFrame, + audioServerBuffer_->GetAvailableDataFrames(), spanSizeInFrame_); + if (audioServerBuffer_->GetAvailableDataFrames() <= spanSizeInFrame_) { + AUDIO_INFO_LOG("OverFlow!!!"); + stream_->DequeueBuffer(length); + stateListener->OnOperationHandled(UPDATE_STREAM, currentReadFrame); + return; + } + BufferDesc srcBuffer = stream_->DequeueBuffer(length); + { + BufferDesc dstBuffer = {nullptr, 0, 0}; + uint64_t curWritePos = audioServerBuffer_->GetCurWriteFrame(); + int32_t ret = audioServerBuffer_->GetWriteBuffer(curWritePos, dstBuffer); + if (ret < 0) { + return; + } + AUDIO_INFO_LOG("On read spanSizeInBytes_ %{public}zu", spanSizeInBytes_); + memcpy_s(dstBuffer.buffer, spanSizeInBytes_, srcBuffer.buffer, spanSizeInBytes_); + + uint64_t nextWriteFrame = currentWriteFrame + spanSizeInFrame_; + AUDIO_INFO_LOG("Read data, current write frame: %{public}" PRIu64 ", next write frame: %{public}" PRIu64 "", + currentWriteFrame, nextWriteFrame); + audioServerBuffer_->SetCurWriteFrame(nextWriteFrame); + } + stream_->EnqueueBuffer(srcBuffer); + stateListener->OnOperationHandled(UPDATE_STREAM, currentReadFrame); +} + +int32_t CapturerInServer::OnReadData(size_t length) +{ + AUDIO_INFO_LOG("CapturerInServer::OnReadData, length: %{public}zu", length); + + ReadData(length); + return SUCCESS; +} + +int32_t CapturerInServer::UpdateReadIndex() +{ + AUDIO_INFO_LOG("UpdateReadIndex: audioServerBuffer_->GetAvailableDataFrames(): %{public}d, needStart: %{public}d", + audioServerBuffer_->GetAvailableDataFrames(), needStart); + return SUCCESS; +} + +int32_t CapturerInServer::ResolveBuffer(std::shared_ptr &buffer) +{ + buffer = audioServerBuffer_; + return SUCCESS; +} + +int32_t CapturerInServer::GetSessionId(uint32_t &sessionId) +{ + { + std::unique_lock lock(statusLock_); + CHECK_AND_RETURN_RET_LOG(status_ != I_STATUS_RELEASED && status_ != I_STATUS_IDLE, ERR_ILLEGAL_STATE, + "Illegal state %{public}d", status_); + } + CHECK_AND_RETURN_RET_LOG(stream_ != nullptr, ERR_OPERATION_FAILED, "GetSessionId failed, stream_ is null"); + sessionId = streamIndex_; + CHECK_AND_RETURN_RET_LOG(sessionId < INT32_MAX, ERR_OPERATION_FAILED, "GetSessionId failed, sessionId:%{public}d", + sessionId); + + return SUCCESS; +} + +int32_t CapturerInServer::Start() +{ + needStart = 0; + std::unique_lock lock(statusLock_); + + if (status_ != I_STATUS_IDLE && status_ != I_STATUS_PAUSED && status_ != I_STATUS_STOPPED) { + AUDIO_ERR_LOG("CapturerInServer::Start failed, Illegal state: %{public}u", status_); + return ERR_ILLEGAL_STATE; + } + status_ = I_STATUS_STARTING; + int ret = stream_->Start(); + CHECK_AND_RETURN_RET_LOG(ret == SUCCESS, ret, "Start stream failed, reason: %{public}d", ret); + resetTime_ = true; + return SUCCESS; +} + +int32_t CapturerInServer::Pause() +{ + std::unique_lock lock(statusLock_); + if (status_ != I_STATUS_STARTED) { + AUDIO_ERR_LOG("CapturerInServer::Pause failed, Illegal state: %{public}u", status_); + return ERR_ILLEGAL_STATE; + } + status_ = I_STATUS_PAUSING; + int ret = stream_->Pause(); + CHECK_AND_RETURN_RET_LOG(ret == SUCCESS, ret, "Pause stream failed, reason: %{public}d", ret); + return SUCCESS; +} + +int32_t CapturerInServer::Flush() +{ + std::unique_lock lock(statusLock_); + if (status_ != I_STATUS_STARTED) { + status_ = I_STATUS_FLUSHING_WHEN_STARTED; + } else if (status_ != I_STATUS_PAUSED) { + status_ = I_STATUS_FLUSHING_WHEN_PAUSED; + } else if (status_ != I_STATUS_STOPPED) { + status_ = I_STATUS_FLUSHING_WHEN_STOPPED; + } else { + AUDIO_ERR_LOG("CapturerInServer::Flush failed, Illegal state: %{public}u", status_); + return ERR_ILLEGAL_STATE; + } + + // Flush buffer of audio server + uint64_t writeFrame = audioServerBuffer_->GetCurWriteFrame(); + uint64_t readFrame = audioServerBuffer_->GetCurReadFrame(); + + while (readFrame < writeFrame) { + BufferDesc bufferDesc = {nullptr, 0, 0}; + int32_t readResult = audioServerBuffer_->GetReadbuffer(readFrame, bufferDesc); + if (readResult != 0) { + return ERR_OPERATION_FAILED; + } + memset_s(bufferDesc.buffer, bufferDesc.bufLength, 0, bufferDesc.bufLength); + readFrame += spanSizeInFrame_; + AUDIO_INFO_LOG("On flush, write frame: %{public}" PRIu64 ", nextReadFrame: %{public}zu," + "readFrame: %{public}" PRIu64 "", writeFrame, spanSizeInFrame_, readFrame); + audioServerBuffer_->SetCurReadFrame(readFrame); + } + + int ret = stream_->Flush(); + CHECK_AND_RETURN_RET_LOG(ret == SUCCESS, ret, "Flush stream failed, reason: %{public}d", ret); + return SUCCESS; +} + +int32_t CapturerInServer::DrainAudioBuffer() +{ + return SUCCESS; +} + +int32_t CapturerInServer::Stop() +{ + std::unique_lock lock(statusLock_); + if (status_ != I_STATUS_STARTED && status_ != I_STATUS_PAUSED) { + AUDIO_ERR_LOG("CapturerInServer::Stop failed, Illegal state: %{public}u", status_); + return ERR_ILLEGAL_STATE; + } + status_ = I_STATUS_STOPPING; + int ret = stream_->Stop(); + CHECK_AND_RETURN_RET_LOG(ret == SUCCESS, ret, "Stop stream failed, reason: %{public}d", ret); + return SUCCESS; +} + +int32_t CapturerInServer::Release() +{ + { + std::unique_lock lock(statusLock_); + CHECK_AND_RETURN_RET_LOG(status_ != I_STATUS_RELEASED && status_ != I_STATUS_IDLE, ERR_ILLEGAL_STATE, + "Illegal state %{public}d", status_); + } + AUDIO_INFO_LOG("Start release capturer"); + { + std::unique_lock lock(statusLock_); + status_ = I_STATUS_RELEASED; + + int32_t ret = IStreamManager::GetRecorderManager().ReleaseCapturer(streamIndex_); + stream_ = nullptr; + if (ret < 0) { + AUDIO_ERR_LOG("Release stream failed, reason: %{public}d", ret); + status_ = I_STATUS_INVALID; + return ret; + } + status_ = I_STATUS_RELEASED; + } + std::shared_ptr stateListener = streamListener_.lock(); + CHECK_AND_RETURN_RET_LOG(stateListener != nullptr, ERROR, "IStreamListener is nullptr"); + stateListener->OnOperationHandled(RELEASE_STREAM, 0); + return SUCCESS; +} + +int32_t CapturerInServer::GetAudioTime(uint64_t &framePos, uint64_t &timeStamp) +{ + if (status_ == I_STATUS_STOPPED) { + AUDIO_WARNING_LOG("Current status is stopped"); + return ERR_ILLEGAL_STATE; + } + stream_->GetStreamFramesRead(framePos); + stream_->GetCurrentTimeStamp(timeStamp); + if (resetTime_) { + resetTime_ = false; + resetTimestamp_ = timeStamp; + } + return SUCCESS; +} + +int32_t CapturerInServer::GetLatency(uint64_t &latency) +{ + return stream_->GetLatency(latency); +} + + +void CapturerInServer::RegisterTestCallback(const std::weak_ptr &callback) +{ + testCallback_ = callback; +} + +int32_t CapturerInServer::ReadOneFrame() +{ + // if buffer ready, get buffer, else, return + size_t minBufferSize = 0; + if (stream_->GetMinimumBufferSize(minBufferSize) < 0) { + AUDIO_ERR_LOG("Get min buffer size err"); + return ERR_OPERATION_FAILED; + } + BufferDesc bufferDesc = stream_->DequeueBuffer(minBufferSize); + stream_->EnqueueBuffer(bufferDesc); + return SUCCESS; +} + +std::shared_ptr CapturerInServer::GetOHSharedBuffer() +{ + return audioServerBuffer_; +} +} // namespace AudioStandard +} // namespace OHOS diff --git a/services/audio_service/server/src/i_stream_manager.cpp b/services/audio_service/server/src/i_stream_manager.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6d90739a614b51e1272dd764b2018494512de934 --- /dev/null +++ b/services/audio_service/server/src/i_stream_manager.cpp @@ -0,0 +1,32 @@ +/* + * 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 "pa_adapter_manager.h" + +namespace OHOS { +namespace AudioStandard { +IStreamManager &IStreamManager::GetPlaybackManager() +{ + static PaAdapterManager adapterManager(PLAYBACK); + return adapterManager; +} + +IStreamManager &IStreamManager::GetRecorderManager() +{ + static PaAdapterManager adapterManager(RECORDER); + return adapterManager; +} +} // namespace AudioStandard +} // namespace OHOS diff --git a/services/audio_service/server/src/ipc_stream_in_server.cpp b/services/audio_service/server/src/ipc_stream_in_server.cpp new file mode 100644 index 0000000000000000000000000000000000000000..409ca971e8cf4a8918734d0e54b0124f9276d6f0 --- /dev/null +++ b/services/audio_service/server/src/ipc_stream_in_server.cpp @@ -0,0 +1,324 @@ +/* + * 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 "ipc_stream_in_server.h" +#include "audio_log.h" +#include "audio_errors.h" + +namespace OHOS { +namespace AudioStandard { +StreamListenerHolder::StreamListenerHolder() +{ + AUDIO_INFO_LOG("StreamListenerHolder()"); +} + +StreamListenerHolder::~StreamListenerHolder() +{ + AUDIO_INFO_LOG("~StreamListenerHolder()"); +} + +int32_t StreamListenerHolder::RegisterStreamListener(sptr listener) +{ + std::lock_guard lock(listenerMutex_); + // should only be set once + if (streamListener_ != nullptr) { + return ERR_INVALID_OPERATION; + } + streamListener_ = listener; + return SUCCESS; +} + +int32_t StreamListenerHolder::OnOperationHandled(Operation operation, int64_t result) +{ + AUDIO_INFO_LOG("OnOperationHandled: operation: %{public}d, result: [%{public}" PRId64 "]", operation, result); + std::lock_guard lock(listenerMutex_); + CHECK_AND_RETURN_RET_LOG(streamListener_ != nullptr, ERR_OPERATION_FAILED, "stream listrener not set"); + return streamListener_->OnOperationHandled(operation, result); +} + +sptr IpcStreamInServer::Create(const AudioProcessConfig &config, int32_t &ret) +{ + AudioMode mode = config.audioMode; + sptr streamInServer = new(std::nothrow) IpcStreamInServer(config, mode); + ret = streamInServer->Config(); + if (ret != SUCCESS) { + AUDIO_ERR_LOG("IpcStreamInServer Config failed: %{public}d, uid: %{public}d", + ret, config.appInfo.appUid); // waiting for review: add uid. + streamInServer = nullptr; + } + return streamInServer; +} + +IpcStreamInServer::IpcStreamInServer(const AudioProcessConfig &config, AudioMode mode) : config_(config), mode_(mode) +{ + AUDIO_INFO_LOG("IpcStreamInServer(), uid: %{public}d", config.appInfo.appUid); // waiting for review: add uid. +} + +IpcStreamInServer::~IpcStreamInServer() +{ + AUDIO_INFO_LOG("~IpcStreamInServer(), uid: %{public}d", config_.appInfo.appUid); // waiting for review: add uid. +} + +int32_t IpcStreamInServer::Config() +{ + streamListenerHolder_ = std::make_shared(); + + // LYH waiting for review: pass streamListenerHolder_ to RendererInServer or CapturerInServer + if (mode_ == AUDIO_MODE_PLAYBACK) { + return ConfigRenderer(); + } + if (mode_ == AUDIO_MODE_RECORD) { + return ConfigCapturer(); + } + AUDIO_ERR_LOG("Config failed, mode is %{public}d", static_cast(mode_)); + return ERR_OPERATION_FAILED; +} + +int32_t IpcStreamInServer::ConfigRenderer() +{ + // LYH waiting for review: use config_.streamInfo instead of AudioStreamParams + rendererInServer_ = std::make_shared(config_, streamListenerHolder_); + rendererInServer_->Init(); + CHECK_AND_RETURN_RET_LOG(rendererInServer_ != nullptr, ERR_OPERATION_FAILED, "create RendererInServer failed"); + return SUCCESS; +} + +int32_t IpcStreamInServer::ConfigCapturer() +{ + // LYH waiting for review: use config_.streamInfo instead of AudioStreamParams + capturerInServer_ = std::make_shared(config_, streamListenerHolder_); + CHECK_AND_RETURN_RET_LOG(capturerInServer_ != nullptr, ERR_OPERATION_FAILED, "create CapturerInServer failed"); + capturerInServer_->Init(); + return SUCCESS; +} + +int32_t IpcStreamInServer::RegisterStreamListener(sptr object) +{ + CHECK_AND_RETURN_RET_LOG(streamListenerHolder_ != nullptr, ERR_OPERATION_FAILED, "RegisterStreamListener failed"); + sptr listener = iface_cast(object); + CHECK_AND_RETURN_RET_LOG(listener != nullptr, ERR_INVALID_PARAM, "RegisterStreamListener obj cast failed"); + streamListenerHolder_->RegisterStreamListener(listener); + + // in plan: get session id, use it as key to find IpcStreamInServer + // in plan: listener->AddDeathRecipient( server ) // when client died, do release and clear works + + return SUCCESS; +} + +int32_t IpcStreamInServer::ResolveBuffer(std::shared_ptr &buffer) +{ + AUDIO_INFO_LOG("Resolve buffer, mode: %{public}d", mode_); + if (mode_ == AUDIO_MODE_PLAYBACK && rendererInServer_ != nullptr) { + return rendererInServer_->ResolveBuffer(buffer); + } + if (mode_ == AUDIO_MODE_RECORD && capturerInServer_!= nullptr) { + return capturerInServer_->ResolveBuffer(buffer); + } + AUDIO_ERR_LOG("GetAudioSessionID failed, invalid mode: %{public}d", static_cast(mode_)); + return ERR_OPERATION_FAILED; +} + +int32_t IpcStreamInServer::UpdatePosition() +{ + if (mode_ == AUDIO_MODE_PLAYBACK && rendererInServer_ != nullptr) { + return rendererInServer_->UpdateWriteIndex(); + } + if (mode_ == AUDIO_MODE_RECORD && capturerInServer_!= nullptr) { + return capturerInServer_->UpdateReadIndex(); + } + AUDIO_ERR_LOG("UpdatePosition failed, invalid mode: %{public}d", static_cast(mode_)); + return ERR_OPERATION_FAILED; +} + +int32_t IpcStreamInServer::GetAudioSessionID(uint32_t &sessionId) +{ + if (mode_ == AUDIO_MODE_PLAYBACK && rendererInServer_ != nullptr) { + return rendererInServer_->GetSessionId(sessionId); + } + if (mode_ == AUDIO_MODE_RECORD && capturerInServer_!= nullptr) { + return capturerInServer_->GetSessionId(sessionId); + } + AUDIO_ERR_LOG("GetAudioSessionID failed, invalid mode: %{public}d", static_cast(mode_)); + return ERR_OPERATION_FAILED; +} + +int32_t IpcStreamInServer::Start() +{ + AUDIO_INFO_LOG("IpcStreamInServer::Start()"); + + if (mode_ == AUDIO_MODE_PLAYBACK && rendererInServer_ != nullptr) { + return rendererInServer_->Start(); + } + if (mode_ == AUDIO_MODE_RECORD && capturerInServer_!= nullptr) { + return capturerInServer_->Start(); + } + AUDIO_ERR_LOG("Start failed, invalid mode: %{public}d", static_cast(mode_)); + return ERR_OPERATION_FAILED; +} + +int32_t IpcStreamInServer::Pause() +{ + if (mode_ == AUDIO_MODE_PLAYBACK && rendererInServer_ != nullptr) { + return rendererInServer_->Pause(); + } + if (mode_ == AUDIO_MODE_RECORD && capturerInServer_!= nullptr) { + return capturerInServer_->Pause(); + } + AUDIO_ERR_LOG("Pause failed, invalid mode: %{public}d", static_cast(mode_)); + return ERR_OPERATION_FAILED; +} + +int32_t IpcStreamInServer::Stop() +{ + if (mode_ == AUDIO_MODE_PLAYBACK && rendererInServer_ != nullptr) { + return rendererInServer_->Stop(); + } + if (mode_ == AUDIO_MODE_RECORD && capturerInServer_!= nullptr) { + return capturerInServer_->Stop(); + } + AUDIO_ERR_LOG("Stop failed, invalid mode: %{public}d", static_cast(mode_)); + return ERR_OPERATION_FAILED; +} + +int32_t IpcStreamInServer::Release() +{ + if (mode_ == AUDIO_MODE_PLAYBACK && rendererInServer_ != nullptr) { + return rendererInServer_->Release(); + } + if (mode_ == AUDIO_MODE_RECORD && capturerInServer_!= nullptr) { + return capturerInServer_->Release(); + } + AUDIO_ERR_LOG("Release failed, invalid mode: %{public}d", static_cast(mode_)); + return ERR_OPERATION_FAILED; +} + +int32_t IpcStreamInServer::Flush() +{ + if (mode_ == AUDIO_MODE_PLAYBACK && rendererInServer_ != nullptr) { + return rendererInServer_->Flush(); + } + if (mode_ == AUDIO_MODE_RECORD && capturerInServer_!= nullptr) { + return capturerInServer_->Flush(); + } + AUDIO_ERR_LOG("Flush failed, invalid mode: %{public}d", static_cast(mode_)); + return ERR_OPERATION_FAILED; +} + +int32_t IpcStreamInServer::Drain() +{ + if (mode_ == AUDIO_MODE_PLAYBACK && rendererInServer_ != nullptr) { + return rendererInServer_->Drain(); + } + AUDIO_ERR_LOG("Drain failed, invalid mode: %{public}d", static_cast(mode_)); + return ERR_OPERATION_FAILED; +} + +int32_t IpcStreamInServer::GetAudioTime(uint64_t &framePos, uint64_t &timeStamp) +{ + if (mode_ == AUDIO_MODE_PLAYBACK && rendererInServer_ != nullptr) { + return rendererInServer_->GetAudioTime(framePos, timeStamp); + } + if (mode_ == AUDIO_MODE_RECORD && capturerInServer_!= nullptr) { + return capturerInServer_->GetAudioTime(framePos, timeStamp); + } + AUDIO_ERR_LOG("GetAudioTime failed, invalid mode: %{public}d", static_cast(mode_)); + return ERR_OPERATION_FAILED; +} + +int32_t IpcStreamInServer::GetLatency(uint64_t &latency) +{ + if (mode_ == AUDIO_MODE_PLAYBACK && rendererInServer_ != nullptr) { + return rendererInServer_->GetLatency(latency); + } + if (mode_ == AUDIO_MODE_RECORD && capturerInServer_!= nullptr) { + return capturerInServer_->GetLatency(latency); + } + AUDIO_ERR_LOG("GetAudioSessionID failed, invalid mode: %{public}d", static_cast(mode_)); + return ERR_OPERATION_FAILED; +} + +int32_t IpcStreamInServer::SetRate(int32_t rate) +{ + if (mode_ == AUDIO_MODE_PLAYBACK && rendererInServer_ != nullptr) { + return rendererInServer_->SetRate(rate); + } + AUDIO_ERR_LOG("SetRate failed, invalid mode: %{public}d", static_cast(mode_)); + return ERR_OPERATION_FAILED; +} + +int32_t IpcStreamInServer::GetRate(int32_t &rate) +{ + // In plan + return ERR_OPERATION_FAILED; +} + +int32_t IpcStreamInServer::SetLowPowerVolume(float volume) +{ + if (mode_ == AUDIO_MODE_PLAYBACK && rendererInServer_ != nullptr) { + return rendererInServer_->SetLowPowerVolume(volume); + } + AUDIO_ERR_LOG("SetLowPowerVolume failed, invalid mode: %{public}d", static_cast(mode_)); + return ERR_OPERATION_FAILED; +} + +int32_t IpcStreamInServer::GetLowPowerVolume(float &volume) +{ + if (mode_ == AUDIO_MODE_PLAYBACK && rendererInServer_ != nullptr) { + return rendererInServer_->GetLowPowerVolume(volume); + } + AUDIO_ERR_LOG("GetLowPowerVolume failed, invalid mode: %{public}d", static_cast(mode_)); + return ERR_OPERATION_FAILED; +} + +int32_t IpcStreamInServer::SetAudioEffectMode(int32_t effectMode) +{ + if (mode_ == AUDIO_MODE_PLAYBACK && rendererInServer_ != nullptr) { + return rendererInServer_->SetAudioEffectMode(effectMode); + } + AUDIO_ERR_LOG("SetAudioEffectMode failed, invalid mode: %{public}d", static_cast(mode_)); + return ERR_OPERATION_FAILED; +} + +int32_t IpcStreamInServer::GetAudioEffectMode(int32_t &effectMode) +{ + if (mode_ == AUDIO_MODE_PLAYBACK && rendererInServer_ != nullptr) { + return rendererInServer_->GetAudioEffectMode(effectMode); + } + AUDIO_ERR_LOG("GetAudioEffectMode failed, invalid mode: %{public}d", static_cast(mode_)); + return ERR_OPERATION_FAILED; +} + +int32_t IpcStreamInServer::SetPrivacyType(int32_t privacyType) +{ + if (mode_ == AUDIO_MODE_PLAYBACK && rendererInServer_ != nullptr) { + return rendererInServer_->SetPrivacyType(privacyType); + } + AUDIO_ERR_LOG("SetPrivacyType failed, invalid mode: %{public}d", static_cast(mode_)); + return ERR_OPERATION_FAILED; +} + +int32_t IpcStreamInServer::GetPrivacyType(int32_t &privacyType) +{ + if (mode_ == AUDIO_MODE_PLAYBACK && rendererInServer_ != nullptr) { + return rendererInServer_->GetPrivacyType(privacyType); + } + AUDIO_ERR_LOG("GetPrivacyType failed, invalid mode: %{public}d", static_cast(mode_)); + return ERR_OPERATION_FAILED; +} +} // namespace AudioStandard +} // namespace OHOS diff --git a/services/audio_service/server/src/ipc_stream_listener_proxy.cpp b/services/audio_service/server/src/ipc_stream_listener_proxy.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3ad6f8b05eba75b84b555169e44ff7010b762f80 --- /dev/null +++ b/services/audio_service/server/src/ipc_stream_listener_proxy.cpp @@ -0,0 +1,48 @@ +/* + * 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 "ipc_stream_listener_proxy.h" +#include "audio_log.h" +#include "audio_errors.h" + +namespace OHOS { +namespace AudioStandard { +IpcStreamListenerProxy::IpcStreamListenerProxy(const sptr &impl) : IRemoteProxy(impl) +{ + AUDIO_INFO_LOG("IpcStreamListenerProxy()"); +} + +IpcStreamListenerProxy::~IpcStreamListenerProxy() +{ + AUDIO_INFO_LOG("~IpcStreamListenerProxy()"); +} + +int32_t IpcStreamListenerProxy::OnOperationHandled(Operation operation, int64_t result) +{ + MessageParcel data; + MessageParcel reply; + MessageOption option(MessageOption::TF_ASYNC); // server call client in async + + CHECK_AND_RETURN_RET_LOG(data.WriteInterfaceToken(GetDescriptor()), ERR_OPERATION_FAILED, + "Write descriptor failed!"); + + data.WriteInt32(operation); + data.WriteInt64(result); + int ret = Remote()->SendRequest(IpcStreamListenerMsg::ON_OPERATION_HANDLED, data, reply, option); + CHECK_AND_RETURN_RET_LOG(ret == AUDIO_OK, ERR_OPERATION_FAILED, "OnEndpointChange failed, error: %{public}d", ret); + return reply.ReadInt32(); +} +} // namespace AudioStandard +} // namespace OHOS diff --git a/services/audio_service/server/src/ipc_stream_stub.cpp b/services/audio_service/server/src/ipc_stream_stub.cpp new file mode 100644 index 0000000000000000000000000000000000000000..eecba50acc51bbf6d4ab3e8a4534b8509f103e38 --- /dev/null +++ b/services/audio_service/server/src/ipc_stream_stub.cpp @@ -0,0 +1,225 @@ +/* + * 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 "ipc_stream_stub.h" +#include "audio_log.h" +#include "audio_errors.h" + +namespace OHOS { +namespace AudioStandard { +bool IpcStreamStub::CheckInterfaceToken(MessageParcel &data) +{ + static auto localDescriptor = IpcStream::GetDescriptor(); + auto remoteDescriptor = data.ReadInterfaceToken(); + if (remoteDescriptor != localDescriptor) { + AUDIO_ERR_LOG("CheckInterFfaceToken failed."); + return false; + } + return true; +} + +int IpcStreamStub::OnRemoteRequest(uint32_t code, MessageParcel &data, MessageParcel &reply, MessageOption &option) +{ + if (!CheckInterfaceToken(data)) { + return AUDIO_ERR; + } + if (code >= IpcStreamMsg::IPC_STREAM_MAX_MSG) { + AUDIO_WARNING_LOG("OnRemoteRequest unsupported request code:%{public}d.", code); + return IPCObjectStub::OnRemoteRequest(code, data, reply, option); + } + return (this->*funcList_[code])(data, reply); +} + +int32_t IpcStreamStub::HandleRegisterStreamListener(MessageParcel &data, MessageParcel &reply) +{ + sptr object = data.ReadRemoteObject(); + if (object == nullptr) { + AUDIO_ERR_LOG("IpcStreamStub: HandleRegisterProcessCb obj is null"); + reply.WriteInt32(AUDIO_INVALID_PARAM); + return AUDIO_INVALID_PARAM; + } + reply.WriteInt32(RegisterStreamListener(object)); + return AUDIO_OK; +} + +int32_t IpcStreamStub::HandleResolveBuffer(MessageParcel &data, MessageParcel &reply) +{ + (void)data; + std::shared_ptr buffer; + int32_t ret = ResolveBuffer(buffer); + reply.WriteInt32(ret); + if (ret == AUDIO_OK && buffer != nullptr) { + OHAudioBuffer::WriteToParcel(buffer, reply); + } else { + AUDIO_ERR_LOG("error: ResolveBuffer failed."); + return AUDIO_INVALID_PARAM; + } + + return AUDIO_OK; +} + +int32_t IpcStreamStub::HandleUpdatePosition(MessageParcel &data, MessageParcel &reply) +{ + AUDIO_INFO_LOG("IpcStreamStub::HandleUpdatePosition"); + (void)data; + reply.WriteInt32(UpdatePosition()); + return AUDIO_OK; +} + +int32_t IpcStreamStub::HandleGetAudioSessionID(MessageParcel &data, MessageParcel &reply) +{ + (void)data; + uint32_t sessionId = 0; + reply.WriteInt32(GetAudioSessionID(sessionId)); + reply.WriteUint32(sessionId); + + return AUDIO_OK; +} + +int32_t IpcStreamStub::HandleStart(MessageParcel &data, MessageParcel &reply) +{ + (void)data; + reply.WriteInt32(Start()); + return AUDIO_OK; +} + +int32_t IpcStreamStub::HandlePause(MessageParcel &data, MessageParcel &reply) +{ + (void)data; + reply.WriteInt32(Pause()); + return AUDIO_OK; +} + +int32_t IpcStreamStub::HandleStop(MessageParcel &data, MessageParcel &reply) +{ + (void)data; + reply.WriteInt32(Stop()); + return AUDIO_OK; +} +int32_t IpcStreamStub::HandleRelease(MessageParcel &data, MessageParcel &reply) +{ + (void)data; + reply.WriteInt32(Release()); + return AUDIO_OK; +} + +int32_t IpcStreamStub::HandleFlush(MessageParcel &data, MessageParcel &reply) +{ + (void)data; + reply.WriteInt32(Flush()); + return AUDIO_OK; +} + +int32_t IpcStreamStub::HandleDrain(MessageParcel &data, MessageParcel &reply) +{ + (void)data; + reply.WriteInt32(Drain()); + return AUDIO_OK; +} + +int32_t IpcStreamStub::HandleGetAudioTime(MessageParcel &data, MessageParcel &reply) +{ + (void)data; + uint64_t framePos = 0; + uint64_t timeStamp = 0; + reply.WriteInt32(GetAudioTime(framePos, timeStamp)); + reply.WriteUint64(framePos); + reply.WriteInt64(timeStamp); + return AUDIO_OK; +} + +int32_t IpcStreamStub::HandleGetLatency(MessageParcel &data, MessageParcel &reply) +{ + (void)data; + uint64_t latency = 0; + reply.WriteInt32(GetLatency(latency)); + reply.WriteUint64(latency); + + return AUDIO_OK; +} + +int32_t IpcStreamStub::HandleSetRate(MessageParcel &data, MessageParcel &reply) +{ + int32_t rate = data.ReadInt32(); + reply.WriteInt32(SetRate(rate)); + + return AUDIO_OK; +} + +int32_t IpcStreamStub::HandleGetRate(MessageParcel &data, MessageParcel &reply) +{ + (void)data; + int32_t rate = -1; + reply.WriteInt32(GetRate(rate)); + reply.WriteInt32(rate); + + return AUDIO_OK; +} + +int32_t IpcStreamStub::HandleSetLowPowerVolume(MessageParcel &data, MessageParcel &reply) +{ + float lowPowerVolume = data.ReadFloat(); + reply.WriteInt32(SetLowPowerVolume(lowPowerVolume)); + + return AUDIO_OK; +} + +int32_t IpcStreamStub::HandleGetLowPowerVolume(MessageParcel &data, MessageParcel &reply) +{ + (void)data; + float lowPowerVolume = 0.0; + reply.WriteInt32(GetLowPowerVolume(lowPowerVolume)); + reply.WriteFloat(lowPowerVolume); + + return AUDIO_OK; +} + +int32_t IpcStreamStub::HandleSetAudioEffectMode(MessageParcel &data, MessageParcel &reply) +{ + int32_t effectMode = data.ReadInt32(); + reply.WriteInt32(SetAudioEffectMode(effectMode)); + + return AUDIO_OK; +} + +int32_t IpcStreamStub::HandleGetAudioEffectMode(MessageParcel &data, MessageParcel &reply) +{ + (void)data; + int32_t effectMode = -1; + reply.WriteInt32(GetAudioEffectMode(effectMode)); + reply.WriteInt32(effectMode); + + return AUDIO_OK; +} + +int32_t IpcStreamStub::HandleSetPrivacyType(MessageParcel &data, MessageParcel &reply) +{ + int32_t privacyType = data.ReadInt32(); + reply.WriteInt32(SetPrivacyType(privacyType)); + + return AUDIO_OK; +} + +int32_t IpcStreamStub::HandleGetPrivacyType(MessageParcel &data, MessageParcel &reply) +{ + (void)data; + int32_t privacyType = -1; + reply.WriteInt32(GetPrivacyType(privacyType)); + reply.WriteInt32(privacyType); + + return AUDIO_OK; +} +} // namespace AudioStandard +} // namespace OHOS diff --git a/services/audio_service/server/src/pa_adapter_manager.cpp b/services/audio_service/server/src/pa_adapter_manager.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b318933142d6931f7e1283e802c3df5eeacf2f4e --- /dev/null +++ b/services/audio_service/server/src/pa_adapter_manager.cpp @@ -0,0 +1,615 @@ +/* + * 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 "pa_adapter_manager.h" +#include +#include +#include "audio_log.h" +#include "audio_errors.h" +#include "pa_renderer_stream_impl.h" +#include "pa_capturer_stream_impl.h" +#include "audio_utils.h" + +namespace OHOS { +namespace AudioStandard { +const uint32_t CHECK_UTIL_SUCCESS = 0; +const uint64_t BUF_LENGTH_IN_MSEC = 20; +static const std::unordered_map STREAM_TYPE_ENUM_STRING_MAP = { + {STREAM_VOICE_CALL, "voice_call"}, + {STREAM_MUSIC, "music"}, + {STREAM_RING, "ring"}, + {STREAM_MEDIA, "media"}, + {STREAM_VOICE_ASSISTANT, "voice_assistant"}, + {STREAM_SYSTEM, "system"}, + {STREAM_ALARM, "alarm"}, + {STREAM_NOTIFICATION, "notification"}, + {STREAM_BLUETOOTH_SCO, "bluetooth_sco"}, + {STREAM_ENFORCED_AUDIBLE, "enforced_audible"}, + {STREAM_DTMF, "dtmf"}, + {STREAM_TTS, "tts"}, + {STREAM_ACCESSIBILITY, "accessibility"}, + {STREAM_RECORDING, "recording"}, + {STREAM_MOVIE, "movie"}, + {STREAM_GAME, "game"}, + {STREAM_SPEECH, "speech"}, + {STREAM_SYSTEM_ENFORCED, "system_enforced"}, + {STREAM_ULTRASONIC, "ultrasonic"}, + {STREAM_WAKEUP, "wakeup"}, + {STREAM_VOICE_MESSAGE, "voice_message"}, + {STREAM_NAVIGATION, "navigation"} +}; + +static int32_t CheckReturnIfinvalid(bool expr, const int32_t retVal) +{ + do { + if (!(expr)) { + return retVal; + } + } while (false); + return CHECK_UTIL_SUCCESS; +} + +PaAdapterManager::PaAdapterManager(ManagerType type) +{ + AUDIO_DEBUG_LOG("Constructor PaAdapterManager"); + mainLoop_ = nullptr; + api_ = nullptr; + context_ = nullptr; + isContextConnected_ = false; + isMainLoopStarted_ = false; + managerType_ = type; +} + +std::atomic g_sessionId = {100000}; // begin at 100000 + +int32_t PaAdapterManager::CreateRender(AudioProcessConfig processConfig, std::shared_ptr &stream) +{ + AUDIO_DEBUG_LOG("Create renderer start"); + if (context_ == nullptr) { + AUDIO_INFO_LOG("Context is null, start to create context"); + int32_t ret = InitPaContext(); + CHECK_AND_RETURN_RET_LOG(ret == SUCCESS, ret, "Failed to init pa context"); + } + + uint32_t sessionId = g_sessionId++; + pa_stream *paStream = InitPaStream(processConfig, sessionId); + std::shared_ptr rendererStream = CreateRendererStream(processConfig, paStream); + CHECK_AND_RETURN_RET_LOG(rendererStream != nullptr, ERR_DEVICE_INIT, "Failed to init pa stream"); + rendererStream->SetStreamIndex(sessionId); + stream = rendererStream; + return SUCCESS; +} + +int32_t PaAdapterManager::ReleaseRender(uint32_t streamIndex) +{ + AUDIO_DEBUG_LOG("Enter ReleaseRender"); + auto it = rendererStreamMap_.find(streamIndex); + if (it == rendererStreamMap_.end()) { + AUDIO_WARNING_LOG("No matching stream"); + return SUCCESS; + } + + if (rendererStreamMap_[streamIndex]->Release() < 0) { + AUDIO_WARNING_LOG("Release stream %{public}d failed", streamIndex); + return ERR_OPERATION_FAILED; + } + rendererStreamMap_[streamIndex] = nullptr; + rendererStreamMap_.erase(streamIndex); + + AUDIO_INFO_LOG("rendererStreamMap_.size() : %{public}zu", rendererStreamMap_.size()); + if (rendererStreamMap_.size() == 0) { + AUDIO_INFO_LOG("Release the last stream"); + if (ResetPaContext() < 0) { + AUDIO_ERR_LOG("Release pa context falied"); + return ERR_OPERATION_FAILED; + } + } + return SUCCESS; +} + +int32_t PaAdapterManager::CreateCapturer(AudioProcessConfig processConfig, std::shared_ptr &stream) +{ + AUDIO_DEBUG_LOG("Create capturer start"); + if (context_ == nullptr) { + AUDIO_INFO_LOG("Context is null, start to create context"); + int32_t ret = InitPaContext(); + CHECK_AND_RETURN_RET_LOG(ret == SUCCESS, ret, "Failed to init pa context"); + } + uint32_t sessionId = g_sessionId++; + pa_stream *paStream = InitPaStream(processConfig, sessionId); + std::shared_ptr capturerStream = CreateCapturerStream(processConfig, paStream); + CHECK_AND_RETURN_RET_LOG(capturerStream != nullptr, ERR_DEVICE_INIT, "Failed to init pa stream"); + capturerStream->SetStreamIndex(sessionId); + stream = capturerStream; + return SUCCESS; +} + +int32_t PaAdapterManager::ReleaseCapturer(uint32_t streamIndex) +{ + AUDIO_DEBUG_LOG("Enter ReleaseCapturer"); + auto it = capturerStreamMap_.find(streamIndex); + if (it == capturerStreamMap_.end()) { + AUDIO_WARNING_LOG("No matching stream"); + return SUCCESS; + } + + if (capturerStreamMap_[streamIndex]->Release() < 0) { + AUDIO_WARNING_LOG("Release stream %{public}d failed", streamIndex); + return ERR_OPERATION_FAILED; + } + + capturerStreamMap_[streamIndex] = nullptr; + capturerStreamMap_.erase(streamIndex); + if (capturerStreamMap_.size() == 0) { + AUDIO_INFO_LOG("Release the last stream"); + if (ResetPaContext() < 0) { + AUDIO_ERR_LOG("Release pa context falied"); + return ERR_OPERATION_FAILED; + } + } + return SUCCESS; +} + +int32_t PaAdapterManager::ResetPaContext() +{ + AUDIO_DEBUG_LOG("Enter ResetPaContext"); + if (context_) { + pa_context_set_state_callback(context_, nullptr, nullptr); + if (isContextConnected_ == true) { + pa_threaded_mainloop_lock(mainLoop_); + pa_context_disconnect(context_); + pa_context_unref(context_); + pa_threaded_mainloop_unlock(mainLoop_); + isContextConnected_ = false; + context_ = nullptr; + } + } + + if (mainLoop_) { + pa_threaded_mainloop_free(mainLoop_); + isMainLoopStarted_ = false; + mainLoop_ = nullptr; + } + + api_ = nullptr; + return SUCCESS; +} + +int32_t PaAdapterManager::InitPaContext() +{ + AUDIO_DEBUG_LOG("Enter InitPaContext"); + int error = ERROR; + mainLoop_ = pa_threaded_mainloop_new(); + CHECK_AND_RETURN_RET_LOG(mainLoop_ != nullptr, ERR_DEVICE_INIT, "Failed to init pa mainLoop"); + api_ = pa_threaded_mainloop_get_api(mainLoop_); + if (api_ == nullptr) { + pa_threaded_mainloop_free(mainLoop_); + AUDIO_ERR_LOG("Get api from mainLoop failed"); + return ERR_DEVICE_INIT; + } + + std::stringstream ss; + ss << "app-pid<" << getpid() << ">-uid<" << getuid() << ">"; + std::string packageName = ""; + ss >> packageName; + + context_ = pa_context_new(api_, packageName.c_str()); + if (context_ == nullptr) { + pa_threaded_mainloop_free(mainLoop_); + AUDIO_ERR_LOG("New context failed"); + return ERR_DEVICE_INIT; + } + + pa_context_set_state_callback(context_, PAContextStateCb, mainLoop_); + if (pa_context_connect(context_, nullptr, PA_CONTEXT_NOFAIL, nullptr) < 0) { + error = pa_context_errno(context_); + AUDIO_ERR_LOG("Context connect error: %{public}s", pa_strerror(error)); + return ERR_DEVICE_INIT; + } + isContextConnected_ = true; + HandleMainLoopStart(); + + pa_threaded_mainloop_unlock(mainLoop_); + return SUCCESS; +} + +int32_t PaAdapterManager::HandleMainLoopStart() +{ + int error = ERROR; + pa_threaded_mainloop_lock(mainLoop_); + if (pa_threaded_mainloop_start(mainLoop_) < 0) { + pa_threaded_mainloop_unlock(mainLoop_); + return ERR_DEVICE_INIT; + } + isMainLoopStarted_ = true; + + while (true) { + pa_context_state_t state = pa_context_get_state(context_); + if (state == PA_CONTEXT_READY) { + AUDIO_INFO_LOG("pa context is ready"); + break; + } + + if (!PA_CONTEXT_IS_GOOD(state)) { + error = pa_context_errno(context_); + AUDIO_ERR_LOG("Context bad state error: %{public}s", pa_strerror(error)); + pa_threaded_mainloop_unlock(mainLoop_); + ResetPaContext(); + return ERR_DEVICE_INIT; + } + pa_threaded_mainloop_wait(mainLoop_); + } + return SUCCESS; +} + +pa_stream *PaAdapterManager::InitPaStream(AudioProcessConfig processConfig, uint32_t sessionId) +{ + AUDIO_DEBUG_LOG("Enter InitPaStream"); + int32_t error = ERROR; + if (CheckReturnIfinvalid(mainLoop_ && context_, ERR_ILLEGAL_STATE) < 0) { + return nullptr; + } + pa_threaded_mainloop_lock(mainLoop_); + + // Use struct to save spec size + pa_sample_spec sampleSpec = ConvertToPAAudioParams(processConfig); + pa_proplist *propList = pa_proplist_new(); + if (propList == nullptr) { + AUDIO_ERR_LOG("pa_proplist_new failed"); + pa_threaded_mainloop_unlock(mainLoop_); + return nullptr; + } + const std::string streamName = GetStreamName(processConfig.streamType); + pa_channel_map map; + CHECK_AND_RETURN_RET_LOG(SetPaProplist(propList, map, processConfig, streamName, sessionId) == 0, nullptr, + "set pa proplist failed"); + + pa_stream *paStream = pa_stream_new_with_proplist(context_, streamName.c_str(), &sampleSpec, nullptr, propList); + if (!paStream) { + error = pa_context_errno(context_); + pa_proplist_free(propList); + pa_threaded_mainloop_unlock(mainLoop_); + AUDIO_ERR_LOG("pa_stream_new_with_proplist failed, error: %{public}d", error); + return nullptr; + } + + pa_proplist_free(propList); + pa_stream_set_state_callback(paStream, PAStreamStateCb, (void *)this); + pa_threaded_mainloop_unlock(mainLoop_); + + int32_t ret = ConnectStreamToPA(paStream, sampleSpec); + if (ret < 0) { + AUDIO_ERR_LOG("ConnectStreamToPA Failed"); + return nullptr; + } + return paStream; +} + +int32_t PaAdapterManager::SetPaProplist(pa_proplist *propList, pa_channel_map &map, AudioProcessConfig &processConfig, + const std::string &streamName, uint32_t sessionId) +{ + // for remote audio device router filter + pa_proplist_sets(propList, "stream.sessionID", std::to_string(sessionId).c_str()); + pa_proplist_sets(propList, "stream.client.uid", std::to_string(processConfig.appInfo.appUid).c_str()); + pa_proplist_sets(propList, "stream.client.pid", std::to_string(processConfig.appInfo.appPid).c_str()); + pa_proplist_sets(propList, "stream.type", streamName.c_str()); + pa_proplist_sets(propList, "media.name", streamName.c_str()); + const std::string effectSceneName = GetEffectSceneName(processConfig.streamType); + pa_proplist_sets(propList, "scene.type", effectSceneName.c_str()); + float mVolumeFactor = 1.0f; + float mPowerVolumeFactor = 1.0f; + pa_proplist_sets(propList, "stream.volumeFactor", std::to_string(mVolumeFactor).c_str()); + pa_proplist_sets(propList, "stream.powerVolumeFactor", std::to_string(mPowerVolumeFactor).c_str()); + auto timenow = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()); + const std::string streamStartTime = ctime(&timenow); + pa_proplist_sets(propList, "stream.startTime", streamStartTime.c_str()); + + if (processConfig.audioMode == AUDIO_MODE_PLAYBACK) { + AudioPrivacyType privacyType = PRIVACY_TYPE_PUBLIC; + pa_proplist_sets(propList, "stream.privacyType", std::to_string(privacyType).c_str()); + pa_proplist_sets(propList, "stream.usage", std::to_string(processConfig.rendererInfo.streamUsage).c_str()); + } else if (processConfig.audioMode == AUDIO_MODE_RECORD) { + pa_proplist_sets(propList, "stream.isInnerCapturer", std::to_string(processConfig.isInnerCapturer).c_str()); + pa_proplist_sets(propList, "stream.isWakeupCapturer", std::to_string(processConfig.isWakeupCapturer).c_str()); + pa_proplist_sets(propList, "stream.capturerSource", + std::to_string(processConfig.capturerInfo.sourceType).c_str()); + } + + AUDIO_INFO_LOG("Creating stream of channels %{public}d", processConfig.streamInfo.channels); + if (processConfig.streamInfo.channelLayout == 0) { + processConfig.streamInfo.channelLayout = defaultChCountToLayoutMap[processConfig.streamInfo.channels]; + } + pa_proplist_sets(propList, "stream.channelLayout", std::to_string(processConfig.streamInfo.channelLayout).c_str()); + + pa_channel_map_init(&map); + map.channels = processConfig.streamInfo.channels; + uint32_t channelsInLayout = ConvertChLayoutToPaChMap(processConfig.streamInfo.channelLayout, map); + if (channelsInLayout != processConfig.streamInfo.channels || channelsInLayout == 0) { + AUDIO_ERR_LOG("Invalid channel Layout"); + return ERR_INVALID_PARAM; + } + return SUCCESS; +} + +std::shared_ptr PaAdapterManager::CreateRendererStream(AudioProcessConfig processConfig, + pa_stream *paStream) +{ + std::shared_ptr rendererStream = + std::make_shared(paStream, processConfig, mainLoop_); + if (rendererStream == nullptr) { + AUDIO_ERR_LOG("Create rendererStream Failed"); + return nullptr; + } + uint32_t streamIndex = pa_stream_get_index(paStream); + AUDIO_DEBUG_LOG("PaStream index is %{public}u", streamIndex); + rendererStreamMap_[streamIndex] = rendererStream; + return rendererStream; +} + +std::shared_ptr PaAdapterManager::CreateCapturerStream(AudioProcessConfig processConfig, + pa_stream *paStream) +{ + std::shared_ptr capturerStream = + std::make_shared(paStream, processConfig, mainLoop_); + if (capturerStream == nullptr) { + AUDIO_ERR_LOG("Create capturerStream Failed"); + return nullptr; + } + uint32_t streamIndex = pa_stream_get_index(paStream); + AUDIO_DEBUG_LOG("PaStream index is %{public}u", streamIndex); + capturerStreamMap_[streamIndex] = capturerStream; + return capturerStream; +} + +int32_t PaAdapterManager::ConnectStreamToPA(pa_stream *paStream, pa_sample_spec sampleSpec) +{ + AUDIO_DEBUG_LOG("Enter PaAdapterManager::ConnectStreamToPA"); + if (CheckReturnIfinvalid(mainLoop_ && context_ && paStream, ERROR) < 0) { + return ERR_ILLEGAL_STATE; + } + + pa_threaded_mainloop_lock(mainLoop_); + if (managerType_ == PLAYBACK) { + int32_t rendererRet = ConnectRendererStreamToPA(paStream, sampleSpec); + CHECK_AND_RETURN_RET_LOG(rendererRet == SUCCESS, rendererRet, "ConnectRendererStreamToPA failed"); + } else { + int32_t capturerRet = ConnectCapturerStreamToPA(paStream, sampleSpec); + CHECK_AND_RETURN_RET_LOG(capturerRet == SUCCESS, capturerRet, "ConnectCapturerStreamToPA failed"); + } + while (true) { + pa_stream_state_t state = pa_stream_get_state(paStream); + if (state == PA_STREAM_READY) { + AUDIO_INFO_LOG("PaStream is ready"); + break; + } + if (!PA_STREAM_IS_GOOD(state)) { + int32_t error = pa_context_errno(context_); + pa_threaded_mainloop_unlock(mainLoop_); + AUDIO_ERR_LOG("connection to stream error: %{public}d", error); + return ERR_INVALID_OPERATION; + } + pa_threaded_mainloop_wait(mainLoop_); + } + pa_threaded_mainloop_unlock(mainLoop_); + return SUCCESS; +} + +int32_t PaAdapterManager::ConnectRendererStreamToPA(pa_stream *paStream, pa_sample_spec sampleSpec) +{ + uint32_t tlength = 3; // 3 is tlength of playback + uint32_t maxlength = 4; // 4 is max buffer length of playback + uint32_t prebuf = 1; // 1 is prebuf of playback + pa_buffer_attr bufferAttr; + bufferAttr.fragsize = static_cast(-1); + bufferAttr.prebuf = pa_usec_to_bytes(BUF_LENGTH_IN_MSEC * PA_USEC_PER_MSEC * prebuf, &sampleSpec); + bufferAttr.maxlength = pa_usec_to_bytes(BUF_LENGTH_IN_MSEC * PA_USEC_PER_MSEC * maxlength, &sampleSpec); + bufferAttr.tlength = pa_usec_to_bytes(BUF_LENGTH_IN_MSEC * PA_USEC_PER_MSEC * tlength, &sampleSpec); + bufferAttr.minreq = pa_usec_to_bytes(BUF_LENGTH_IN_MSEC * PA_USEC_PER_MSEC, &sampleSpec); + AUDIO_INFO_LOG("bufferAttr, maxLength: %{public}u, tlength: %{public}u, prebuf: %{public}u", + maxlength, tlength, prebuf); + + int32_t result = pa_stream_connect_playback(paStream, nullptr, &bufferAttr, + (pa_stream_flags_t)(PA_STREAM_ADJUST_LATENCY | PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_START_CORKED | + PA_STREAM_VARIABLE_RATE), nullptr, nullptr); + if (result < 0) { + int32_t error = pa_context_errno(context_); + AUDIO_ERR_LOG("connection to stream error: %{public}d", error); + pa_threaded_mainloop_unlock(mainLoop_); + return ERR_INVALID_OPERATION; + } + return SUCCESS; +} + +int32_t PaAdapterManager::ConnectCapturerStreamToPA(pa_stream *paStream, pa_sample_spec sampleSpec) +{ + uint32_t fragsize = 1; // 1 is frag size of recorder + uint32_t maxlength = 3; // 3 is max buffer length of recorder + pa_buffer_attr bufferAttr; + bufferAttr.maxlength = pa_usec_to_bytes(BUF_LENGTH_IN_MSEC * PA_USEC_PER_MSEC * maxlength, &sampleSpec); + bufferAttr.fragsize = pa_usec_to_bytes(BUF_LENGTH_IN_MSEC * PA_USEC_PER_MSEC * fragsize, &sampleSpec); + AUDIO_INFO_LOG("bufferAttr, maxLength: %{public}d, fragsize: %{public}d", + bufferAttr.maxlength, bufferAttr.fragsize); + + int32_t result = pa_stream_connect_record(paStream, nullptr, &bufferAttr, + (pa_stream_flags_t)(PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_START_CORKED | + PA_STREAM_VARIABLE_RATE)); + // PA_STREAM_ADJUST_LATENCY exist, return peek length from server; + if (result < 0) { + int32_t error = pa_context_errno(context_); + AUDIO_ERR_LOG("connection to stream error: %{public}d", error); + pa_threaded_mainloop_unlock(mainLoop_); + return ERR_INVALID_OPERATION; + } + return SUCCESS; +} + +void PaAdapterManager::PAStreamUpdateStreamIndexSuccessCb(pa_stream *stream, int32_t success, void *userdata) +{ + AUDIO_DEBUG_LOG("PAStreamUpdateStreamIndexSuccessCb in"); +} + +void PaAdapterManager::PAContextStateCb(pa_context *context, void *userdata) +{ + pa_threaded_mainloop *mainLoop = (pa_threaded_mainloop *)userdata; + AUDIO_INFO_LOG("Current Context State: %{public}d", pa_context_get_state(context)); + + switch (pa_context_get_state(context)) { + case PA_CONTEXT_READY: + pa_threaded_mainloop_signal(mainLoop, 0); + break; + case PA_CONTEXT_TERMINATED: + case PA_CONTEXT_FAILED: + pa_threaded_mainloop_signal(mainLoop, 0); + break; + + case PA_CONTEXT_UNCONNECTED: + case PA_CONTEXT_CONNECTING: + case PA_CONTEXT_AUTHORIZING: + case PA_CONTEXT_SETTING_NAME: + default: + break; + } +} + +void PaAdapterManager::PAStreamStateCb(pa_stream *stream, void *userdata) +{ + if (!userdata) { + AUDIO_ERR_LOG("PAStreamStateCb: userdata is null"); + return; + } + PaAdapterManager *adapterManger = (PaAdapterManager *)userdata; + AUDIO_INFO_LOG("Current Stream State: %{public}d", pa_stream_get_state(stream)); + switch (pa_stream_get_state(stream)) { + case PA_STREAM_READY: + case PA_STREAM_FAILED: + case PA_STREAM_TERMINATED: + pa_threaded_mainloop_signal(adapterManger->mainLoop_, 0); + break; + case PA_STREAM_UNCONNECTED: + case PA_STREAM_CREATING: + default: + break; + } +} + +const std::string PaAdapterManager::GetStreamName(AudioStreamType audioType) +{ + std::string name = "unknown"; + if (STREAM_TYPE_ENUM_STRING_MAP.find(audioType) != STREAM_TYPE_ENUM_STRING_MAP.end()) { + name = STREAM_TYPE_ENUM_STRING_MAP.at(audioType); + } else { + AUDIO_ERR_LOG("GetStreamName: Invalid stream type [%{public}d], return unknown", audioType); + } + const std::string streamName = name; + return streamName; +} + +pa_sample_spec PaAdapterManager::ConvertToPAAudioParams(AudioProcessConfig processConfig) +{ + pa_sample_spec paSampleSpec; + paSampleSpec.channels = processConfig.streamInfo.channels; + paSampleSpec.rate = processConfig.streamInfo.samplingRate; + switch (processConfig.streamInfo.format) { + case SAMPLE_U8: + paSampleSpec.format = (pa_sample_format_t)PA_SAMPLE_U8; + break; + case SAMPLE_S16LE: + paSampleSpec.format = (pa_sample_format_t)PA_SAMPLE_S16LE; + break; + case SAMPLE_S24LE: + paSampleSpec.format = (pa_sample_format_t)PA_SAMPLE_S24LE; + break; + case SAMPLE_S32LE: + paSampleSpec.format = (pa_sample_format_t)PA_SAMPLE_S32LE; + break; + default: + paSampleSpec.format = (pa_sample_format_t)PA_SAMPLE_INVALID; + break; + } + return paSampleSpec; +} + + +uint32_t PaAdapterManager::ConvertChLayoutToPaChMap(const uint64_t &channelLayout, pa_channel_map &paMap) +{ + uint32_t channelNum = 0; + uint64_t mode = (channelLayout & CH_MODE_MASK) >> CH_MODE_OFFSET; + switch (mode) { + case 0: { + for (auto bit = chSetToPaPositionMap.begin(); bit != chSetToPaPositionMap.end(); ++bit) { + if ((channelLayout & (bit->first)) != 0) { + paMap.map[channelNum++] = bit->second; + } + } + break; + } + case 1: { + uint64_t order = (channelLayout & CH_HOA_ORDNUM_MASK) >> CH_HOA_ORDNUM_OFFSET; + channelNum = (order + 1) * (order + 1); + for (uint32_t i = 0; i < channelNum; ++i) { + paMap.map[i] = chSetToPaPositionMap[FRONT_LEFT]; + } + break; + } + default: + channelNum = 0; + break; + } + return channelNum; +} + +const std::string PaAdapterManager::GetEffectSceneName(AudioStreamType audioType) +{ + std::string name; + switch (audioType) { + case STREAM_MUSIC: + name = "SCENE_MUSIC"; + break; + case STREAM_GAME: + name = "SCENE_GAME"; + break; + case STREAM_MOVIE: + name = "SCENE_MOVIE"; + break; + case STREAM_SPEECH: + case STREAM_VOICE_CALL: + case STREAM_VOICE_ASSISTANT: + name = "SCENE_SPEECH"; + break; + case STREAM_RING: + case STREAM_ALARM: + case STREAM_NOTIFICATION: + case STREAM_SYSTEM: + case STREAM_DTMF: + case STREAM_SYSTEM_ENFORCED: + name = "SCENE_RING"; + break; + default: + name = "SCENE_OTHERS"; + } + + const std::string sceneName = name; + return sceneName; +} + + +int32_t PaAdapterManager::GetInfo() +{ + AUDIO_INFO_LOG("pa_context_get_state(),: %{public}d, pa_context_errno(): %{public}d", + pa_context_get_state(context_), pa_context_errno(context_)); + return SUCCESS; +} +} // namespace AudioStandard +} // namespace OHOS diff --git a/services/audio_service/server/src/pa_capturer_stream_impl.cpp b/services/audio_service/server/src/pa_capturer_stream_impl.cpp new file mode 100644 index 0000000000000000000000000000000000000000..64e2d5e861fd08ab4c4facbb0f4bafafd13761f4 --- /dev/null +++ b/services/audio_service/server/src/pa_capturer_stream_impl.cpp @@ -0,0 +1,483 @@ +/* + * 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 "pa_capturer_stream_impl.h" +#include "audio_errors.h" +#include "audio_log.h" + +namespace OHOS { +namespace AudioStandard { +static int32_t CheckReturnIfStreamInvalid(pa_stream *paStream, const int32_t retVal) +{ + do { + if (!(paStream && PA_STREAM_IS_GOOD(pa_stream_get_state(paStream)))) { + return retVal; + } + } while (false); + return SUCCESS; +} + +PaCapturerStreamImpl::PaCapturerStreamImpl(pa_stream *paStream, AudioProcessConfig processConfig, + pa_threaded_mainloop *mainloop) +{ + mainloop_ = mainloop; + paStream_ = paStream; + processConfig_ = processConfig; + + pa_stream_set_moved_callback(paStream, PAStreamMovedCb, (void *)this); // used to notify sink/source moved + pa_stream_set_read_callback(paStream, PAStreamReadCb, (void *)this); + pa_stream_set_underflow_callback(paStream, PAStreamUnderFlowCb, (void *)this); + pa_stream_set_started_callback(paStream, PAStreamSetStartedCb, (void *)this); + + InitParams(); +} + +inline uint32_t PcmFormatToBits(uint8_t format) +{ + switch (format) { + case SAMPLE_U8: + return 1; // 1 byte + case SAMPLE_S16LE: + return 2; // 2 byt + case SAMPLE_S24LE: + return 3; // 3 byte + case SAMPLE_S32LE: + return 4; // 4 byte + case SAMPLE_F32LE: + return 4; // 4 byte + default: + return 2; // 2 byte + } +} + +void PaCapturerStreamImpl::InitParams() +{ + if (CheckReturnIfStreamInvalid(paStream_, ERROR) < 0) { + return; + } + + // Get byte size per frame + const pa_sample_spec *sampleSpec = pa_stream_get_sample_spec(paStream_); + if (sampleSpec->channels != processConfig_.streamInfo.channels) { + AUDIO_WARNING_LOG("Unequal channels, in server: %{public}d, in client: %{public}d", sampleSpec->channels, + processConfig_.streamInfo.channels); + } + if (static_cast(sampleSpec->format) != processConfig_.streamInfo.format) { + AUDIO_WARNING_LOG("Unequal format, in server: %{public}d, in client: %{public}d", sampleSpec->format, + processConfig_.streamInfo.format); + } + byteSizePerFrame_ = pa_frame_size(sampleSpec); + + // Get min buffer size in frame + const pa_buffer_attr *bufferAttr = pa_stream_get_buffer_attr(paStream_); + if (bufferAttr == nullptr) { + AUDIO_ERR_LOG("pa_stream_get_buffer_attr returned nullptr"); + return; + } + minBufferSize_ = (size_t)bufferAttr->fragsize; + spanSizeInFrame_ = minBufferSize_ / byteSizePerFrame_; + AUDIO_INFO_LOG("byteSizePerFrame_ %{public}zu, spanSizeInFrame_ %{public}zu, minBufferSize_ %{public}zu", + byteSizePerFrame_, spanSizeInFrame_, minBufferSize_); + + capturerServerDumpFile_ = fopen("/data/data/.pulse_dir/capturer_impl.pcm", "wb+"); +} + +int32_t PaCapturerStreamImpl::Start() +{ + AUDIO_DEBUG_LOG("Enter PaCapturerStreamImpl::Start"); + if (CheckReturnIfStreamInvalid(paStream_, ERROR) < 0) { + return ERR_ILLEGAL_STATE; + } + pa_operation *operation = nullptr; + pa_stream_state_t state = pa_stream_get_state(paStream_); + if (state != PA_STREAM_READY) { + return ERR_ILLEGAL_STATE; + } + + streamCmdStatus_ = 0; + operation = pa_stream_cork(paStream_, 0, PAStreamStartSuccessCb, (void *)this); + pa_operation_unref(operation); + return SUCCESS; +} + +int32_t PaCapturerStreamImpl::Pause() +{ + AUDIO_INFO_LOG("Enter PaCapturerStreamImpl::Pause"); + PAStreamCorkSuccessCb = PAStreamPauseSuccessCb; + return CorkStream(); +} + +int32_t PaCapturerStreamImpl::CorkStream() +{ + if (CheckReturnIfStreamInvalid(paStream_, ERROR) < 0) { + return ERR_ILLEGAL_STATE; + } + pa_operation *operation = nullptr; + + pa_stream_state_t state = pa_stream_get_state(paStream_); + if (state != PA_STREAM_READY) { + AUDIO_ERR_LOG("Stream Stop Failed"); + return ERR_ILLEGAL_STATE; + } + operation = pa_stream_cork(paStream_, 1, PAStreamCorkSuccessCb, (void *)this); + pa_operation_unref(operation); + return SUCCESS; +} + +int32_t PaCapturerStreamImpl::GetStreamFramesRead(uint64_t &framesRead) +{ + if (byteSizePerFrame_ == 0) { + AUDIO_ERR_LOG("Error frame size"); + return ERR_OPERATION_FAILED; + } + framesRead = totalBytesRead_ / byteSizePerFrame_; + return SUCCESS; +} + +int32_t PaCapturerStreamImpl::GetCurrentTimeStamp(uint64_t &timeStamp) +{ + if (CheckReturnIfStreamInvalid(paStream_, ERROR) < 0) { + return ERR_ILLEGAL_STATE; + } + pa_threaded_mainloop_lock(mainloop_); + + pa_operation *operation = pa_stream_update_timing_info(paStream_, NULL, NULL); + if (operation != nullptr) { + while (pa_operation_get_state(operation) == PA_OPERATION_RUNNING) { + pa_threaded_mainloop_wait(mainloop_); + } + pa_operation_unref(operation); + } else { + AUDIO_ERR_LOG("pa_stream_update_timing_info failed"); + } + + const pa_timing_info *info = pa_stream_get_timing_info(paStream_); + if (info == nullptr) { + AUDIO_ERR_LOG("pa_stream_get_timing_info failed"); + pa_threaded_mainloop_unlock(mainloop_); + return ERR_OPERATION_FAILED; + } + + if (pa_stream_get_time(paStream_, &timeStamp)) { + AUDIO_ERR_LOG("GetCurrentTimeStamp failed for AUDIO_SERVICE_CLIENT_RECORD"); + pa_threaded_mainloop_unlock(mainloop_); + return ERR_OPERATION_FAILED; + } + int32_t uid = static_cast(getuid()); + const pa_sample_spec *sampleSpec = pa_stream_get_sample_spec(paStream_); + timeStamp = pa_bytes_to_usec(info->write_index, sampleSpec); + // 1013 is media_service's uid + int32_t media_service = 1013; + if (uid == media_service) { + timeStamp = pa_bytes_to_usec(totalBytesRead_, sampleSpec); + } + pa_threaded_mainloop_unlock(mainloop_); + return SUCCESS; +} + +int32_t PaCapturerStreamImpl::GetLatency(uint64_t &latency) +{ + // LYH in plan, 增加dataMutex的锁 + if (CheckReturnIfStreamInvalid(paStream_, ERROR) < 0) { + return ERR_ILLEGAL_STATE; + } + pa_usec_t paLatency {0}; + pa_usec_t cacheLatency {0}; + int32_t negative {0}; + + // Get PA latency + pa_threaded_mainloop_lock(mainloop_); + while (true) { + pa_operation *operation = pa_stream_update_timing_info(paStream_, NULL, NULL); + if (operation != nullptr) { + pa_operation_unref(operation); + } else { + AUDIO_ERR_LOG("pa_stream_update_timing_info failed"); + } + if (pa_stream_get_latency(paStream_, &paLatency, &negative) >= 0) { + if (negative) { + latency = 0; + pa_threaded_mainloop_unlock(mainloop_); + return ERR_OPERATION_FAILED; + } + break; + } + AUDIO_INFO_LOG("waiting for audio latency information"); + pa_threaded_mainloop_wait(mainloop_); + } + pa_threaded_mainloop_unlock(mainloop_); + + // In plan, 怎么计算cacheLatency + // Get audio read cache latency + const pa_sample_spec *sampleSpec = pa_stream_get_sample_spec(paStream_); + cacheLatency = pa_bytes_to_usec(minBufferSize_, sampleSpec); + + // Total latency will be sum of audio read cache latency + PA latency + latency = paLatency + cacheLatency; + AUDIO_DEBUG_LOG("total latency: %{public}" PRIu64 ", pa latency: %{public}" PRIu64, latency, paLatency); + return SUCCESS; +} + +int32_t PaCapturerStreamImpl::Flush() +{ + AUDIO_INFO_LOG("Enter PaCapturerStreamImpl::Flush"); + if (CheckReturnIfStreamInvalid(paStream_, ERROR) < 0) { + return ERR_ILLEGAL_STATE; + } + + pa_operation *operation = nullptr; + pa_stream_state_t state = pa_stream_get_state(paStream_); + if (state != PA_STREAM_READY) { + AUDIO_ERR_LOG("Stream Flush Failed"); + return ERR_ILLEGAL_STATE; + } + streamFlushStatus_ = 0; + operation = pa_stream_flush(paStream_, PAStreamFlushSuccessCb, (void *)this); + if (operation == nullptr) { + AUDIO_ERR_LOG("Stream Flush Operation Failed"); + return ERR_OPERATION_FAILED; + } + pa_operation_unref(operation); + return SUCCESS; +} + +int32_t PaCapturerStreamImpl::Stop() +{ + AUDIO_INFO_LOG("Enter PaCapturerStreamImpl::Stop"); + PAStreamCorkSuccessCb = PAStreamStopSuccessCb; + return CorkStream(); +} + +int32_t PaCapturerStreamImpl::Release() +{ + state_ = RELEASED; + if (paStream_) { + pa_stream_set_state_callback(paStream_, nullptr, nullptr); + pa_stream_set_read_callback(paStream_, nullptr, nullptr); + pa_stream_set_read_callback(paStream_, nullptr, nullptr); + pa_stream_set_latency_update_callback(paStream_, nullptr, nullptr); + pa_stream_set_underflow_callback(paStream_, nullptr, nullptr); + + pa_stream_disconnect(paStream_); + pa_stream_unref(paStream_); + paStream_ = nullptr; + } + return SUCCESS; +} + +void PaCapturerStreamImpl::RegisterStatusCallback(const std::weak_ptr &callback) +{ + statusCallback_ = callback; +} + +void PaCapturerStreamImpl::RegisterReadCallback(const std::weak_ptr &callback) +{ + AUDIO_INFO_LOG("RegisterReadCallback start"); + readCallback_ = callback; +} + +BufferDesc PaCapturerStreamImpl::DequeueBuffer(size_t length) +{ + BufferDesc bufferDesc; + const void *tempBuffer = nullptr; + pa_stream_peek(paStream_, &tempBuffer, &bufferDesc.bufLength); + bufferDesc.buffer = static_cast(const_cast(tempBuffer)); + totalBytesRead_ += bufferDesc.bufLength; + AUDIO_INFO_LOG("PaCapturerStreamImpl::DequeueBuffer length %{public}zu", bufferDesc.bufLength); + fwrite(reinterpret_cast(bufferDesc.buffer), 1, bufferDesc.bufLength, capturerServerDumpFile_); + return bufferDesc; +} + +int32_t PaCapturerStreamImpl::EnqueueBuffer(const BufferDesc &bufferDesc) +{ + AUDIO_INFO_LOG("Capturer enqueue buffer"); + pa_stream_drop(paStream_); + AUDIO_INFO_LOG("After enqueue capturere buffer, readable size is %{public}zu", pa_stream_readable_size(paStream_)); + return SUCCESS; +} + +int32_t PaCapturerStreamImpl::DropBuffer() +{ + AUDIO_INFO_LOG("Capturer DropBuffer"); + pa_stream_drop(paStream_); + AUDIO_INFO_LOG("After capturere DropBuffer, readable size is %{public}zu", pa_stream_readable_size(paStream_)); + return SUCCESS; +} + +void PaCapturerStreamImpl::PAStreamReadCb(pa_stream *stream, size_t length, void *userdata) +{ + AUDIO_INFO_LOG("PAStreamReadCb, length size: %{public}zu, pa_stream_readable_size: %{public}zu", + length, pa_stream_readable_size(stream)); + + const pa_buffer_attr *bufferAttr = pa_stream_get_buffer_attr(stream); + AUDIO_INFO_LOG("Buffer attr: maxlength %{public}d, tlength %{public}d, prebuf %{public}d, minreq %{public}d," + "fragsize %{public}d", bufferAttr->maxlength, bufferAttr->tlength, bufferAttr->prebuf, + bufferAttr->minreq, bufferAttr->fragsize); + if (!userdata) { + AUDIO_ERR_LOG("PAStreamReadCb: userdata is null"); + return; + } + auto streamImpl = static_cast(userdata); + if (streamImpl->abortFlag_ != 0) { + AUDIO_ERR_LOG("PAStreamReadCb: Abort pa stream read callback"); + streamImpl->abortFlag_--; + return ; + } + std::shared_ptr readCallback = streamImpl->readCallback_.lock(); + if (readCallback != nullptr) { + readCallback->OnReadData(length); + } else { + AUDIO_ERR_LOG("Read callback is nullptr"); + } +} + +void PaCapturerStreamImpl::PAStreamMovedCb(pa_stream *stream, void *userdata) +{ + if (!userdata) { + AUDIO_ERR_LOG("PAStreamMovedCb: userdata is null"); + return; + } + + // get stream informations. + uint32_t deviceIndex = pa_stream_get_device_index(stream); // pa_context_get_sink_info_by_index + + // Return 1 if the sink or source this stream is connected to has been suspended. + // This will return 0 if not, and a negative value on error. + int res = pa_stream_is_suspended(stream); + AUDIO_DEBUG_LOG("PAstream moved to index:[%{public}d] suspended:[%{public}d]", + deviceIndex, res); +} + +void PaCapturerStreamImpl::PAStreamUnderFlowCb(pa_stream *stream, void *userdata) +{ + if (!userdata) { + AUDIO_ERR_LOG("PAStreamUnderFlowCb: userdata is null"); + return; + } + + PaCapturerStreamImpl *streamImpl = static_cast(userdata); + streamImpl->underFlowCount_++; + + std::shared_ptr statusCallback = streamImpl->statusCallback_.lock(); + if (statusCallback != nullptr) { + statusCallback->OnStatusUpdate(OPERATION_UNDERRUN); + } + AUDIO_WARNING_LOG("PaCapturerStreamImpl underrun: %{public}d!", streamImpl->underFlowCount_); +} + + +void PaCapturerStreamImpl::PAStreamSetStartedCb(pa_stream *stream, void *userdata) +{ + if (!userdata) { + AUDIO_ERR_LOG("PAStreamSetStartedCb: userdata is null"); + return; + } + + AUDIO_WARNING_LOG("PAStreamSetStartedCb"); +} + +void PaCapturerStreamImpl::PAStreamStartSuccessCb(pa_stream *stream, int32_t success, void *userdata) +{ + if (!userdata) { + AUDIO_ERR_LOG("PAStreamStartSuccessCb: userdata is null"); + return; + } + + PaCapturerStreamImpl *streamImpl = static_cast(userdata); + streamImpl->state_ = RUNNING; + std::shared_ptr statusCallback = streamImpl->statusCallback_.lock(); + if (statusCallback != nullptr) { + statusCallback->OnStatusUpdate(OPERATION_STARTED); + } + streamImpl->streamCmdStatus_ = success; +} + +void PaCapturerStreamImpl::PAStreamPauseSuccessCb(pa_stream *stream, int32_t success, void *userdata) +{ + if (!userdata) { + AUDIO_ERR_LOG("PAStreamPauseSuccessCb: userdata is null"); + return; + } + + PaCapturerStreamImpl *streamImpl = static_cast(userdata); + streamImpl->state_ = PAUSED; + std::shared_ptr statusCallback = streamImpl->statusCallback_.lock(); + if (statusCallback != nullptr) { + statusCallback->OnStatusUpdate(OPERATION_PAUSED); + } + streamImpl->streamCmdStatus_ = success; +} + +void PaCapturerStreamImpl::PAStreamFlushSuccessCb(pa_stream *stream, int32_t success, void *userdata) +{ + if (!userdata) { + AUDIO_ERR_LOG("PAStreamFlushSuccessCb: userdata is null"); + return; + } + PaCapturerStreamImpl *streamImpl = static_cast(userdata); + std::shared_ptr statusCallback = streamImpl->statusCallback_.lock(); + if (statusCallback != nullptr) { + statusCallback->OnStatusUpdate(OPERATION_FLUSHED); + } + streamImpl->streamFlushStatus_ = success; +} + +void PaCapturerStreamImpl::PAStreamStopSuccessCb(pa_stream *stream, int32_t success, void *userdata) +{ + if (!userdata) { + AUDIO_ERR_LOG("PAStreamAsyncStopSuccessCb: userdata is null"); + return; + } + + PaCapturerStreamImpl *streamImpl = static_cast(userdata); + std::shared_ptr statusCallback = streamImpl->statusCallback_.lock(); + if (statusCallback != nullptr) { + statusCallback->OnStatusUpdate(OPERATION_STOPPED); + } +} + +int32_t PaCapturerStreamImpl::GetMinimumBufferSize(size_t &minBufferSize) const +{ + minBufferSize = minBufferSize_; + return SUCCESS; +} + +void PaCapturerStreamImpl::GetByteSizePerFrame(size_t &byteSizePerFrame) const +{ + byteSizePerFrame = byteSizePerFrame_; +} + +void PaCapturerStreamImpl::GetSpanSizePerFrame(size_t &spanSizeInFrame) const +{ + spanSizeInFrame = spanSizeInFrame_; +} + +void PaCapturerStreamImpl::SetStreamIndex(uint32_t index) +{ + AUDIO_INFO_LOG("Using index/sessionId %{public}d", index); + streamIndex_ = index; +} + +uint32_t PaCapturerStreamImpl::GetStreamIndex() +{ + return streamIndex_; +} + +void PaCapturerStreamImpl::AbortCallback(int32_t abortTimes) +{ + abortFlag_ += abortTimes; +} +} // namespace AudioStandard +} // namespace OHOS diff --git a/services/audio_service/server/src/pa_renderer_stream_impl.cpp b/services/audio_service/server/src/pa_renderer_stream_impl.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1a29018c5653a4e28f9f2643f3987c95786db8c1 --- /dev/null +++ b/services/audio_service/server/src/pa_renderer_stream_impl.cpp @@ -0,0 +1,687 @@ +/* + * 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 "pa_renderer_stream_impl.h" +#include "audio_errors.h" +#include "audio_log.h" + +namespace OHOS { +namespace AudioStandard { +const uint32_t DOUBLE_VALUE = 2; + + +static int32_t CheckReturnIfStreamInvalid(pa_stream *paStream, const int32_t retVal) +{ + do { + if (!(paStream && PA_STREAM_IS_GOOD(pa_stream_get_state(paStream)))) { + return retVal; + } + } while (false); + return SUCCESS; +} + +PaRendererStreamImpl::PaRendererStreamImpl(pa_stream *paStream, AudioProcessConfig processConfig, + pa_threaded_mainloop *mainloop) +{ + mainloop_ = mainloop; + paStream_ = paStream; + processConfig_ = processConfig; + + pa_stream_set_moved_callback(paStream, PAStreamMovedCb, (void *)this); // used to notify sink/source moved + pa_stream_set_write_callback(paStream, PAStreamWriteCb, (void *)this); + pa_stream_set_underflow_callback(paStream, PAStreamUnderFlowCb, (void *)this); + pa_stream_set_started_callback(paStream, PAStreamSetStartedCb, (void *)this); + + InitParams(); +} + +inline uint32_t PcmFormatToBits(uint8_t format) +{ + switch (format) { + case SAMPLE_U8: + return 1; // 1 byte + case SAMPLE_S16LE: + return 2; // 2 byte + case SAMPLE_S24LE: + return 3; // 3 byte + case SAMPLE_S32LE: + return 4; // 4 byte + case SAMPLE_F32LE: + return 4; // 4 byte + default: + return 2; // 2 byte + } +} + +void PaRendererStreamImpl::InitParams() +{ + if (CheckReturnIfStreamInvalid(paStream_, ERR_ILLEGAL_STATE) < 0) { + return; + } + + // Get byte size per frame + const pa_sample_spec *sampleSpec = pa_stream_get_sample_spec(paStream_); + AUDIO_INFO_LOG("sampleSpec: channels: %{public}u, formats: %{public}d, rate: %{public}d", sampleSpec->channels, + sampleSpec->format, sampleSpec->rate); + + if (sampleSpec->channels != processConfig_.streamInfo.channels) { + AUDIO_WARNING_LOG("Unequal channels, in server: %{public}d, in client: %{public}d", sampleSpec->channels, + processConfig_.streamInfo.channels); + } + if (static_cast(sampleSpec->format) != processConfig_.streamInfo.format) { // In plan + AUDIO_WARNING_LOG("Unequal format, in server: %{public}d, in client: %{public}d", sampleSpec->format, + processConfig_.streamInfo.format); + } + byteSizePerFrame_ = pa_frame_size(sampleSpec); + + // Get min buffer size in frame + const pa_buffer_attr *bufferAttr = pa_stream_get_buffer_attr(paStream_); + if (bufferAttr == nullptr) { + AUDIO_ERR_LOG("pa_stream_get_buffer_attr returned nullptr"); + return; + } + minBufferSize_ = (size_t)bufferAttr->minreq; + spanSizeInFrame_ = minBufferSize_ / byteSizePerFrame_; + + // In plan: Get data from xml + effectSceneName_ = GetEffectSceneName(processConfig_.streamType); +} + +int32_t PaRendererStreamImpl::Start() +{ + AUDIO_DEBUG_LOG("Enter PaRendererStreamImpl::Start"); + if (CheckReturnIfStreamInvalid(paStream_, ERR_ILLEGAL_STATE) < 0) { + return ERR_ILLEGAL_STATE; + } + pa_operation *operation = nullptr; + + pa_stream_state_t state = pa_stream_get_state(paStream_); + if (state != PA_STREAM_READY) { + return ERR_OPERATION_FAILED; + } + + streamCmdStatus_ = 0; + operation = pa_stream_cork(paStream_, 0, PAStreamStartSuccessCb, (void *)this); + pa_operation_unref(operation); + return SUCCESS; +} + +int32_t PaRendererStreamImpl::Pause() +{ + AUDIO_INFO_LOG("Enter PaRendererStreamImpl::Pause"); + PAStreamCorkSuccessCb = PAStreamPauseSuccessCb; + return CorkStream(); +} + +int32_t PaRendererStreamImpl::CorkStream() +{ + if (CheckReturnIfStreamInvalid(paStream_, ERR_ILLEGAL_STATE) < 0) { + return ERR_ILLEGAL_STATE; + } + + pa_operation *operation = nullptr; + pa_stream_state_t state = pa_stream_get_state(paStream_); + if (state != PA_STREAM_READY) { + AUDIO_ERR_LOG("Stream Stop Failed"); + return ERR_OPERATION_FAILED; + } + operation = pa_stream_cork(paStream_, 1, PAStreamCorkSuccessCb, (void *)this); + pa_operation_unref(operation); + return SUCCESS; +} + +int32_t PaRendererStreamImpl::Flush() +{ + AUDIO_INFO_LOG("Enter PaRendererStreamImpl::Flush"); + if (CheckReturnIfStreamInvalid(paStream_, ERR_ILLEGAL_STATE) < 0) { + return ERR_ILLEGAL_STATE; + } + + pa_operation *operation = nullptr; + pa_stream_state_t state = pa_stream_get_state(paStream_); + if (state != PA_STREAM_READY) { + AUDIO_ERR_LOG("Stream Flush Failed"); + return ERR_OPERATION_FAILED; + } + + streamFlushStatus_ = 0; + operation = pa_stream_flush(paStream_, PAStreamFlushSuccessCb, (void *)this); + if (operation == nullptr) { + AUDIO_ERR_LOG("Stream Flush Operation Failed"); + return ERR_OPERATION_FAILED; + } + pa_operation_unref(operation); + return SUCCESS; +} + +int32_t PaRendererStreamImpl::Drain() +{ + AUDIO_INFO_LOG("Enter PaRendererStreamImpl::Drain"); + if (CheckReturnIfStreamInvalid(paStream_, ERR_ILLEGAL_STATE) < 0) { + return ERR_ILLEGAL_STATE; + } + isDrain_ = true; + + pa_operation *operation = nullptr; + pa_stream_state_t state = pa_stream_get_state(paStream_); + if (state != PA_STREAM_READY) { + AUDIO_ERR_LOG("Stream drain failed, state is not ready"); + return ERR_OPERATION_FAILED; + } + streamDrainStatus_ = 0; + operation = pa_stream_drain(paStream_, PAStreamDrainSuccessCb, (void *)this); + pa_operation_unref(operation); + return SUCCESS; +} + +int32_t PaRendererStreamImpl::Stop() +{ + AUDIO_INFO_LOG("Enter PaRendererStreamImpl::Stop"); + state_ = STOPPING; + + if (CheckReturnIfStreamInvalid(paStream_, ERR_ILLEGAL_STATE) < 0) { + return ERR_ILLEGAL_STATE; + } + + streamDrainStatus_ = 0; + isDrain_ = true; + pa_operation *operation = pa_stream_drain(paStream_, PAStreamDrainInStopCb, (void *)this); + if (operation == nullptr) { + AUDIO_ERR_LOG("pa_stream_drain operation is null"); + return ERR_OPERATION_FAILED; + } + + pa_operation_unref(operation); + return SUCCESS; +} + +int32_t PaRendererStreamImpl::Release() +{ + state_ = RELEASED; + if (paStream_) { + pa_stream_set_state_callback(paStream_, nullptr, nullptr); + pa_stream_set_write_callback(paStream_, nullptr, nullptr); + pa_stream_set_read_callback(paStream_, nullptr, nullptr); + pa_stream_set_latency_update_callback(paStream_, nullptr, nullptr); + pa_stream_set_underflow_callback(paStream_, nullptr, nullptr); + + pa_stream_disconnect(paStream_); + pa_stream_unref(paStream_); + paStream_ = nullptr; + } + std::shared_ptr statusCallback = statusCallback_.lock(); + if (statusCallback != nullptr) { + statusCallback->OnStatusUpdate(OPERATION_RELEASED); + } + return SUCCESS; +} + +int32_t PaRendererStreamImpl::GetStreamFramesWritten(uint64_t &framesWritten) +{ + CHECK_AND_RETURN_RET_LOG(byteSizePerFrame_ != 0, ERR_ILLEGAL_STATE, "Error frame size"); + framesWritten = totalBytesWritten_ / byteSizePerFrame_; + return SUCCESS; +} + +int32_t PaRendererStreamImpl::GetCurrentTimeStamp(uint64_t &timeStamp) +{ + if (CheckReturnIfStreamInvalid(paStream_, ERR_ILLEGAL_STATE) < 0) { + return ERR_ILLEGAL_STATE; + } + pa_threaded_mainloop_lock(mainloop_); + + pa_operation *operation = pa_stream_update_timing_info(paStream_, NULL, NULL); + if (operation != nullptr) { + while (pa_operation_get_state(operation) == PA_OPERATION_RUNNING) { + pa_threaded_mainloop_wait(mainloop_); + } + pa_operation_unref(operation); + } else { + AUDIO_ERR_LOG("pa_stream_update_timing_info failed"); + } + + const pa_timing_info *info = pa_stream_get_timing_info(paStream_); + if (info == nullptr) { + AUDIO_ERR_LOG("pa_stream_get_timing_info failed"); + pa_threaded_mainloop_unlock(mainloop_); + return ERR_OPERATION_FAILED; + } + + const pa_sample_spec *sampleSpec = pa_stream_get_sample_spec(paStream_); + timeStamp = pa_bytes_to_usec(info->write_index, sampleSpec); + pa_threaded_mainloop_unlock(mainloop_); + return SUCCESS; +} + +int32_t PaRendererStreamImpl::GetLatency(uint64_t &latency) +{ + // LYH in plan, 增加dataMutex的锁 + if (CheckReturnIfStreamInvalid(paStream_, ERR_ILLEGAL_STATE) < 0) { + return ERR_ILLEGAL_STATE; + } + pa_usec_t paLatency {0}; + pa_usec_t cacheLatency {0}; + int32_t negative {0}; + + // Get PA latency + pa_threaded_mainloop_lock(mainloop_); + while (true) { + pa_operation *operation = pa_stream_update_timing_info(paStream_, NULL, NULL); + if (operation != nullptr) { + pa_operation_unref(operation); + } else { + AUDIO_ERR_LOG("pa_stream_update_timing_info failed"); + } + if (pa_stream_get_latency(paStream_, &paLatency, &negative) >= 0) { + if (negative) { + latency = 0; + pa_threaded_mainloop_unlock(mainloop_); + return ERR_OPERATION_FAILED; + } + break; + } + AUDIO_INFO_LOG("waiting for audio latency information"); + pa_threaded_mainloop_wait(mainloop_); + } + pa_threaded_mainloop_unlock(mainloop_); + cacheLatency = 0; + // In plan: Total latency will be sum of audio write cache latency plus PA latency + uint64_t fwLatency = paLatency + cacheLatency; + uint64_t sinkLatency = sinkLatencyInMsec_ * PA_USEC_PER_MSEC; + if (fwLatency > sinkLatency) { + latency = fwLatency - sinkLatency; + } else { + latency = fwLatency; + } + AUDIO_DEBUG_LOG("total latency: %{public}" PRIu64 ", pa latency: %{public}" + PRIu64 ", cache latency: %{public}" PRIu64, latency, paLatency, cacheLatency); + + return SUCCESS; +} + +int32_t PaRendererStreamImpl::SetRate(int32_t rate) +{ + AUDIO_INFO_LOG("SetRate in"); + if (CheckReturnIfStreamInvalid(paStream_, ERR_ILLEGAL_STATE) < 0) { + return ERR_ILLEGAL_STATE; + } + uint32_t currentRate = processConfig_.streamInfo.samplingRate; + switch (rate) { + case RENDER_RATE_NORMAL: + break; + case RENDER_RATE_DOUBLE: + currentRate *= DOUBLE_VALUE; + break; + case RENDER_RATE_HALF: + currentRate /= DOUBLE_VALUE; + break; + default: + return ERR_INVALID_PARAM; + } + renderRate_ = rate; + + pa_threaded_mainloop_lock(mainloop_); + pa_operation *operation = pa_stream_update_sample_rate(paStream_, currentRate, nullptr, nullptr); + if (operation != nullptr) { + pa_operation_unref(operation); + } else { + AUDIO_ERR_LOG("SetRate: operation is nullptr"); + } + pa_threaded_mainloop_unlock(mainloop_); + return SUCCESS; +} + +int32_t PaRendererStreamImpl::SetLowPowerVolume(float powerVolume) +{ + AUDIO_INFO_LOG("SetLowPowerVolume: %{public}f", powerVolume); + + if (CheckReturnIfStreamInvalid(paStream_, ERR_ILLEGAL_STATE) < 0) { + return ERR_ILLEGAL_STATE; + } + + /* Validate and return INVALID_PARAMS error */ + if ((powerVolume < MIN_STREAM_VOLUME_LEVEL) || (powerVolume > MAX_STREAM_VOLUME_LEVEL)) { + AUDIO_ERR_LOG("Invalid Power Volume Set!"); + return -1; + } + + pa_threaded_mainloop_lock(mainloop_); + + powerVolumeFactor_ = powerVolume; + pa_proplist *propList = pa_proplist_new(); + if (propList == nullptr) { + AUDIO_ERR_LOG("pa_proplist_new failed"); + pa_threaded_mainloop_unlock(mainloop_); + return ERR_OPERATION_FAILED; + } + + pa_proplist_sets(propList, "stream.powerVolumeFactor", std::to_string(powerVolumeFactor_).c_str()); + pa_operation *updatePropOperation = pa_stream_proplist_update(paStream_, PA_UPDATE_REPLACE, propList, + nullptr, nullptr); + pa_proplist_free(propList); + pa_operation_unref(updatePropOperation); + + // In plan: Call reset volume + pa_threaded_mainloop_unlock(mainloop_); + + return SUCCESS; +} + +int32_t PaRendererStreamImpl::GetLowPowerVolume(float &powerVolume) +{ + powerVolume = powerVolumeFactor_; + return SUCCESS; +} + +int32_t PaRendererStreamImpl::SetAudioEffectMode(int32_t effectMode) +{ + AUDIO_INFO_LOG("SetAudioEffectMode: %{public}d", effectMode); + if (CheckReturnIfStreamInvalid(paStream_, ERR_ILLEGAL_STATE) < 0) { + return ERR_ILLEGAL_STATE; + } + pa_threaded_mainloop_lock(mainloop_); + + effectMode_ = effectMode; + const std::string effectModeName = GetEffectModeName(effectMode_); + + pa_proplist *propList = pa_proplist_new(); + if (propList == nullptr) { + AUDIO_ERR_LOG("pa_proplist_new failed"); + pa_threaded_mainloop_unlock(mainloop_); + return ERR_OPERATION_FAILED; + } + + pa_proplist_sets(propList, "scene.mode", effectModeName.c_str()); + pa_operation *updatePropOperation = pa_stream_proplist_update(paStream_, PA_UPDATE_REPLACE, propList, + nullptr, nullptr); + pa_proplist_free(propList); + pa_operation_unref(updatePropOperation); + + pa_threaded_mainloop_unlock(mainloop_); + + return SUCCESS; +} + +const std::string PaRendererStreamImpl::GetEffectModeName(int32_t effectMode) +{ + std::string name; + switch (effectMode) { + case 0: // AudioEffectMode::EFFECT_NONE + name = "EFFECT_NONE"; + break; + default: + name = "EFFECT_DEFAULT"; + } + + const std::string modeName = name; + return modeName; +} + +int32_t PaRendererStreamImpl::GetAudioEffectMode(int32_t &effectMode) +{ + effectMode = effectMode_; + return SUCCESS; +} + +int32_t PaRendererStreamImpl::SetPrivacyType(int32_t privacyType) +{ + AUDIO_DEBUG_LOG("SetInnerCapturerState: %{public}d", privacyType); + privacyType_ = privacyType; + return SUCCESS; +} + +int32_t PaRendererStreamImpl::GetPrivacyType(int32_t &privacyType) +{ + privacyType_ = privacyType; + return SUCCESS; +} + + +void PaRendererStreamImpl::RegisterStatusCallback(const std::weak_ptr &callback) +{ + AUDIO_DEBUG_LOG("RegisterStatusCallback in"); + statusCallback_ = callback; +} + +void PaRendererStreamImpl::RegisterWriteCallback(const std::weak_ptr &callback) +{ + AUDIO_DEBUG_LOG("RegisterWriteCallback in"); + writeCallback_ = callback; +} + +BufferDesc PaRendererStreamImpl::DequeueBuffer(size_t length) +{ + BufferDesc bufferDesc; + bufferDesc.bufLength = length; + pa_stream_begin_write(paStream_, reinterpret_cast(&bufferDesc.buffer), &bufferDesc.bufLength); + return bufferDesc; +} + +int32_t PaRendererStreamImpl::EnqueueBuffer(const BufferDesc &bufferDesc) +{ + pa_threaded_mainloop_lock(mainloop_); + int32_t error = 0; + error = pa_stream_write(paStream_, static_cast(bufferDesc.buffer), bufferDesc.bufLength, nullptr, + 0LL, PA_SEEK_RELATIVE); + if (error < 0) { + AUDIO_ERR_LOG("Write stream failed"); + pa_stream_cancel_write(paStream_); + } + totalBytesWritten_ += bufferDesc.bufLength; + pa_threaded_mainloop_unlock(mainloop_); + return SUCCESS; +} + +void PaRendererStreamImpl::PAStreamWriteCb(pa_stream *stream, size_t length, void *userdata) +{ + AUDIO_INFO_LOG("PAStreamWriteCb, length: %{public}zu, pa_stream_writeable_size: %{public}zu", + length, pa_stream_writable_size(stream)); + CHECK_AND_RETURN_LOG(userdata, "PAStreamWriteCb: userdata is null"); + + auto streamImpl = static_cast(userdata); + if (streamImpl->abortFlag_ != 0) { + AUDIO_ERR_LOG("PAStreamWriteCb: Abort pa stream write callback"); + streamImpl->abortFlag_--; + return ; + } + std::shared_ptr writeCallback = streamImpl->writeCallback_.lock(); + if (writeCallback != nullptr) { + writeCallback->OnWriteData(length); + } else { + AUDIO_ERR_LOG("Write callback is nullptr"); + } +} + +void PaRendererStreamImpl::PAStreamMovedCb(pa_stream *stream, void *userdata) +{ + CHECK_AND_RETURN_LOG(userdata, "PAStreamMovedCb: userdata is null"); + + // get stream informations. + uint32_t deviceIndex = pa_stream_get_device_index(stream); // pa_context_get_sink_info_by_index + + // Return 1 if the sink or source this stream is connected to has been suspended. + // This will return 0 if not, and a negative value on error. + int res = pa_stream_is_suspended(stream); + AUDIO_DEBUG_LOG("PAstream moved to index:[%{public}d] suspended:[%{public}d]", + deviceIndex, res); +} + +void PaRendererStreamImpl::PAStreamUnderFlowCb(pa_stream *stream, void *userdata) +{ + CHECK_AND_RETURN_LOG(userdata, "PAStreamUnderFlowCb: userdata is null"); + + PaRendererStreamImpl *streamImpl = static_cast(userdata); + streamImpl->underFlowCount_++; + std::shared_ptr statusCallback = streamImpl->statusCallback_.lock(); + if (statusCallback != nullptr) { + statusCallback->OnStatusUpdate(OPERATION_UNDERRUN); + } + AUDIO_WARNING_LOG("PaRendererStreamImpl underrun: %{public}d!", streamImpl->underFlowCount_); +} + + +void PaRendererStreamImpl::PAStreamSetStartedCb(pa_stream *stream, void *userdata) +{ + CHECK_AND_RETURN_LOG(userdata, "PAStreamSetStartedCb: userdata is null"); + AUDIO_WARNING_LOG("PAStreamSetStartedCb"); +} + +void PaRendererStreamImpl::PAStreamStartSuccessCb(pa_stream *stream, int32_t success, void *userdata) +{ + AUDIO_INFO_LOG("PAStreamStartSuccessCb in"); + CHECK_AND_RETURN_LOG(userdata, "PAStreamStartSuccessCb: userdata is null"); + + PaRendererStreamImpl *streamImpl = static_cast(userdata); + streamImpl->state_ = RUNNING; + std::shared_ptr statusCallback = streamImpl->statusCallback_.lock(); + if (statusCallback != nullptr) { + statusCallback->OnStatusUpdate(OPERATION_STARTED); + } + streamImpl->streamCmdStatus_ = success; +} + +void PaRendererStreamImpl::PAStreamPauseSuccessCb(pa_stream *stream, int32_t success, void *userdata) +{ + CHECK_AND_RETURN_LOG(userdata, "PAStreamPauseSuccessCb: userdata is null"); + + PaRendererStreamImpl *streamImpl = static_cast(userdata); + streamImpl->state_ = PAUSED; + std::shared_ptr statusCallback = streamImpl->statusCallback_.lock(); + if (statusCallback != nullptr) { + statusCallback->OnStatusUpdate(OPERATION_PAUSED); + } + streamImpl->streamCmdStatus_ = success; +} + +void PaRendererStreamImpl::PAStreamFlushSuccessCb(pa_stream *stream, int32_t success, void *userdata) +{ + CHECK_AND_RETURN_LOG(userdata, "PAStreamFlushSuccessCb: userdata is null"); + + PaRendererStreamImpl *streamImpl = static_cast(userdata); + std::shared_ptr statusCallback = streamImpl->statusCallback_.lock(); + if (statusCallback != nullptr) { + statusCallback->OnStatusUpdate(OPERATION_FLUSHED); + } + streamImpl->streamFlushStatus_ = success; +} + +void PaRendererStreamImpl::PAStreamDrainSuccessCb(pa_stream *stream, int32_t success, void *userdata) +{ + CHECK_AND_RETURN_LOG(userdata, "PAStreamDrainSuccessCb: userdata is null"); + + PaRendererStreamImpl *streamImpl = static_cast(userdata); + std::shared_ptr statusCallback = streamImpl->statusCallback_.lock(); + if (statusCallback != nullptr) { + statusCallback->OnStatusUpdate(OPERATION_DRAINED); + } + streamImpl->streamDrainStatus_ = success; + streamImpl->isDrain_ = false; +} + +void PaRendererStreamImpl::PAStreamDrainInStopCb(pa_stream *stream, int32_t success, void *userdata) +{ + CHECK_AND_RETURN_LOG(userdata, "PAStreamDrainInStopCb: userdata is null"); + + PaRendererStreamImpl *streamImpl = static_cast(userdata); + pa_operation *operation = pa_stream_cork(streamImpl->paStream_, 1, + PaRendererStreamImpl::PAStreamAsyncStopSuccessCb, userdata); + + pa_operation_unref(operation); + streamImpl->streamDrainStatus_ = success; +} + +void PaRendererStreamImpl::PAStreamAsyncStopSuccessCb(pa_stream *stream, int32_t success, void *userdata) +{ + AUDIO_DEBUG_LOG("PAStreamAsyncStopSuccessCb in"); + CHECK_AND_RETURN_LOG(userdata, "PAStreamAsyncStopSuccessCb: userdata is null"); + + PaRendererStreamImpl *streamImpl = static_cast(userdata); + streamImpl->state_ = STOPPED; + std::shared_ptr statusCallback = streamImpl->statusCallback_.lock(); + + if (statusCallback != nullptr) { + statusCallback->OnStatusUpdate(OPERATION_STOPPED); + } + streamImpl->streamCmdStatus_ = success; + streamImpl->isDrain_ = false; +} + +int32_t PaRendererStreamImpl::GetMinimumBufferSize(size_t &minBufferSize) const +{ + minBufferSize = minBufferSize_; + return SUCCESS; +} + +void PaRendererStreamImpl::GetByteSizePerFrame(size_t &byteSizePerFrame) const +{ + byteSizePerFrame = byteSizePerFrame_; +} + +void PaRendererStreamImpl::GetSpanSizePerFrame(size_t &spanSizeInFrame) const +{ + spanSizeInFrame = spanSizeInFrame_; +} + +void PaRendererStreamImpl::SetStreamIndex(uint32_t index) +{ + AUDIO_INFO_LOG("Using index/sessionId %{public}d", index); + streamIndex_ = index; +} + +uint32_t PaRendererStreamImpl::GetStreamIndex() +{ + return streamIndex_; +} + +const std::string PaRendererStreamImpl::GetEffectSceneName(AudioStreamType audioType) +{ + std::string name; + switch (audioType) { + case STREAM_MUSIC: + name = "SCENE_MUSIC"; + break; + case STREAM_GAME: + name = "SCENE_GAME"; + break; + case STREAM_MOVIE: + name = "SCENE_MOVIE"; + break; + case STREAM_SPEECH: + case STREAM_VOICE_CALL: + case STREAM_VOICE_ASSISTANT: + name = "SCENE_SPEECH"; + break; + case STREAM_RING: + case STREAM_ALARM: + case STREAM_NOTIFICATION: + case STREAM_SYSTEM: + case STREAM_DTMF: + case STREAM_SYSTEM_ENFORCED: + name = "SCENE_RING"; + break; + default: + name = "SCENE_OTHERS"; + } + + const std::string sceneName = name; + return sceneName; +} + +void PaRendererStreamImpl::AbortCallback(int32_t abortTimes) +{ + abortFlag_ += abortTimes; +} +} // namespace AudioStandard +} // namespace OHOS diff --git a/services/audio_service/server/src/renderer_in_server.cpp b/services/audio_service/server/src/renderer_in_server.cpp new file mode 100644 index 0000000000000000000000000000000000000000..da6c3acfbe4bc9bbda500f06a6c979550f23c17f --- /dev/null +++ b/services/audio_service/server/src/renderer_in_server.cpp @@ -0,0 +1,470 @@ +/* + * 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 "renderer_in_server.h" +#include +#include "securec.h" +#include "audio_errors.h" +#include "audio_log.h" +#include "i_stream_manager.h" + +namespace OHOS { +namespace AudioStandard { +namespace { + static constexpr int32_t VOLUME_SHIFT_NUMBER = 16; // 1 >> 16 = 65536, max volume +} + +RendererInServer::RendererInServer(AudioProcessConfig processConfig, std::weak_ptr streamListener) +{ + processConfig_ = processConfig; + streamListener_ = streamListener; // LYH wating for review + + int32_t ret = IStreamManager::GetPlaybackManager().CreateRender(processConfig_, stream_); + AUDIO_INFO_LOG("Construct rendererInServer result: %{public}d", ret); + streamIndex_ = stream_->GetStreamIndex(); + ConfigServerBuffer(); +} + +RendererInServer::~RendererInServer() +{ + if (status_ != I_STATUS_RELEASED && status_ != I_STATUS_IDLE) { + Release(); + } +} + +int32_t RendererInServer::ConfigServerBuffer() +{ + if (audioServerBuffer_ != nullptr) { + AUDIO_INFO_LOG("ConfigProcessBuffer: process buffer already configed!"); + return SUCCESS; + } + stream_->GetSpanSizePerFrame(spanSizeInFrame_); + totalSizeInFrame_ = spanSizeInFrame_ * 4; // 4 frames + stream_->GetByteSizePerFrame(byteSizePerFrame_); + if (totalSizeInFrame_ == 0 || spanSizeInFrame_ == 0 || totalSizeInFrame_ % spanSizeInFrame_ != 0) { + AUDIO_ERR_LOG("ConfigProcessBuffer: ERR_INVALID_PARAM"); + return ERR_INVALID_PARAM; + } + + stream_->GetByteSizePerFrame(byteSizePerFrame_); + AUDIO_INFO_LOG("ConfigProcessBuffer: totalSizeInFrame_: %{public}zu, spanSizeInFrame_: %{public}zu," + "byteSizePerFrame_:%{public}zu", totalSizeInFrame_, spanSizeInFrame_, byteSizePerFrame_); + + // create OHAudioBuffer in server + audioServerBuffer_ = OHAudioBuffer::CreateFromLocal(totalSizeInFrame_, spanSizeInFrame_, byteSizePerFrame_); + CHECK_AND_RETURN_RET_LOG(audioServerBuffer_ != nullptr, ERR_OPERATION_FAILED, "Create oh audio buffer failed"); + + // we need to clear data buffer to avoid dirty data. + memset_s(audioServerBuffer_->GetDataBase(), audioServerBuffer_->GetDataSize(), 0, + audioServerBuffer_->GetDataSize()); + int32_t ret = InitBufferStatus(); + AUDIO_DEBUG_LOG("Clear data buffer, ret:%{public}d", ret); + + isBufferConfiged_ = true; + isInited_ = true; + return SUCCESS; +} + +int32_t RendererInServer::InitBufferStatus() +{ + if (audioServerBuffer_ == nullptr) { + AUDIO_ERR_LOG("InitBufferStatus failed, null buffer."); + return ERR_ILLEGAL_STATE; + } + + uint32_t spanCount = audioServerBuffer_->GetSpanCount(); + AUDIO_INFO_LOG("InitBufferStatus: spanCount %{public}u", spanCount); + for (uint32_t i = 0; i < spanCount; i++) { + SpanInfo *spanInfo = audioServerBuffer_->GetSpanInfoByIndex(i); + if (spanInfo == nullptr) { + AUDIO_ERR_LOG("InitBufferStatus failed, null spaninfo"); + return ERR_ILLEGAL_STATE; + } + spanInfo->spanStatus = SPAN_READ_DONE; + spanInfo->offsetInFrame = 0; + + spanInfo->readStartTime = 0; + spanInfo->readDoneTime = 0; + + spanInfo->writeStartTime = 0; + spanInfo->writeDoneTime = 0; + + spanInfo->volumeStart = 1 << VOLUME_SHIFT_NUMBER; // 65536 for initialize + spanInfo->volumeEnd = 1 << VOLUME_SHIFT_NUMBER; // 65536 for initialize + spanInfo->isMute = false; + } + return SUCCESS; +} + +void RendererInServer::Init() +{ + AUDIO_INFO_LOG("Init, register status and write callback"); + CHECK_AND_RETURN_LOG(stream_ != nullptr, "Renderer stream is nullptr"); + stream_->RegisterStatusCallback(shared_from_this()); + stream_->RegisterWriteCallback(shared_from_this()); +} + +void RendererInServer::OnStatusUpdate(IOperation operation) +{ + AUDIO_INFO_LOG("RendererInServer::OnStatusUpdate operation: %{public}d", operation); + operation_ = operation; + CHECK_AND_RETURN_LOG(operation != OPERATION_RELEASED, "Stream already released"); + std::lock_guard lock(statusLock_); + std::shared_ptr stateListener = streamListener_.lock(); + switch (operation) { + case OPERATION_UNDERRUN: + AUDIO_INFO_LOG("Underrun: audioServerBuffer_->GetAvailableDataFrames(): %{public}d", + audioServerBuffer_->GetAvailableDataFrames()); + if (audioServerBuffer_->GetAvailableDataFrames() == 4 * spanSizeInFrame_) { //In plan, maxlength is 4 + AUDIO_INFO_LOG("Buffer is empty"); + needStart = 0; + } else { + AUDIO_INFO_LOG("Buffer is not empty"); + WriteData(); + } + break; + case OPERATION_STARTED: + status_ = I_STATUS_STARTED; + stateListener->OnOperationHandled(START_STREAM, 0); + break; + case OPERATION_PAUSED: + status_ = I_STATUS_PAUSED; + stateListener->OnOperationHandled(PAUSE_STREAM, 0); + break; + case OPERATION_STOPPED: + stateListener->OnOperationHandled(STOP_STREAM, 0); + status_ = I_STATUS_STOPPED; + break; + case OPERATION_FLUSHED: + stateListener->OnOperationHandled(FLUSH_STREAM, 0); + HandleOperationFlushed(); + break; + case OPERATION_DRAINED: + stateListener->OnOperationHandled(DRAIN_STREAM, 0); + status_ = I_STATUS_STARTED; + afterDrain = true; + break; + case OPERATION_RELEASED: + stateListener->OnOperationHandled(RELEASE_STREAM, 0); + status_ = I_STATUS_RELEASED; + break; + default: + AUDIO_INFO_LOG("Invalid operation %{public}u", operation); + status_ = I_STATUS_INVALID; + } +} + +void RendererInServer::HandleOperationFlushed() +{ + switch (status_) { + case I_STATUS_FLUSHING_WHEN_STARTED: + status_ = I_STATUS_STARTED; + break; + case I_STATUS_FLUSHING_WHEN_PAUSED: + status_ = I_STATUS_PAUSED; + break; + case I_STATUS_FLUSHING_WHEN_STOPPED: + status_ = I_STATUS_STOPPED; + break; + default: + AUDIO_WARNING_LOG("Invalid status before flusing"); + } +} + +BufferDesc RendererInServer::DequeueBuffer(size_t length) +{ + return stream_->DequeueBuffer(length); +} + +void RendererInServer::WriteData() +{ + uint64_t currentReadFrame = audioServerBuffer_->GetCurReadFrame(); + uint64_t currentWriteFrame = audioServerBuffer_->GetCurWriteFrame(); + if (currentReadFrame + spanSizeInFrame_ > currentWriteFrame) { + AUDIO_INFO_LOG("Underrun!!!"); + return; + } + BufferDesc bufferDesc = {nullptr, totalSizeInFrame_, totalSizeInFrame_}; + + if (audioServerBuffer_->GetReadbuffer(currentReadFrame, bufferDesc) == SUCCESS) { + AUDIO_INFO_LOG("Buffer length: %{public}zu", bufferDesc.bufLength); + stream_->EnqueueBuffer(bufferDesc); + uint64_t nextReadFrame = currentReadFrame + spanSizeInFrame_; + AUDIO_INFO_LOG("CurrentReadFrame: %{public}" PRIu64 ", nextReadFrame:%{public}" PRIu64 "", currentReadFrame, + nextReadFrame); + audioServerBuffer_->SetCurReadFrame(nextReadFrame); + memset_s(bufferDesc.buffer, bufferDesc.bufLength, 0, bufferDesc.bufLength); // clear is needed for reuse. + } + std::shared_ptr stateListener = streamListener_.lock(); + CHECK_AND_RETURN_LOG(stateListener != nullptr, "IStreamListener is nullptr"); + stateListener->OnOperationHandled(UPDATE_STREAM, currentReadFrame); +} + +void RendererInServer::WriteEmptyData() +{ + AUDIO_WARNING_LOG("Underrun, write empty data"); + BufferDesc bufferDesc = stream_->DequeueBuffer(totalSizeInFrame_); + memset_s(bufferDesc.buffer, bufferDesc.bufLength, 0, bufferDesc.bufLength); + stream_->EnqueueBuffer(bufferDesc); + return; +} + +int32_t RendererInServer::OnWriteData(size_t length) +{ + AUDIO_INFO_LOG("RendererInServer::OnWriteData"); + for (int32_t i = 0; i < length / totalSizeInFrame_; i++) { + WriteData(); + } + return SUCCESS; +} + +int32_t RendererInServer::UpdateWriteIndex() +{ + AUDIO_INFO_LOG("UpdateWriteIndex: audioServerBuffer_->GetAvailableDataFrames(): %{public}d, " + "spanSizeInFrame_:%{public}zu, needStart: %{public}d", audioServerBuffer_->GetAvailableDataFrames(), + spanSizeInFrame_, needStart); + if (audioServerBuffer_->GetAvailableDataFrames() == spanSizeInFrame_ && needStart < 3) { // 3 is maxlength - 1 + AUDIO_WARNING_LOG("Start write data"); + WriteData(); + needStart++; + } + if (afterDrain == true) { + afterDrain = false; + AUDIO_WARNING_LOG("After drain, start write data"); + WriteData(); + } + return SUCCESS; +} + +int32_t RendererInServer::ResolveBuffer(std::shared_ptr &buffer) +{ + buffer = audioServerBuffer_; + return SUCCESS; +} + +int32_t RendererInServer::GetSessionId(uint32_t &sessionId) +{ + CHECK_AND_RETURN_RET_LOG(stream_ != nullptr, ERR_OPERATION_FAILED, "GetSessionId failed, stream_ is null"); + sessionId = streamIndex_; + CHECK_AND_RETURN_RET_LOG(sessionId < INT32_MAX, ERR_OPERATION_FAILED, "GetSessionId failed, sessionId:%{public}d", + sessionId); + + return SUCCESS; +} + +int32_t RendererInServer::Start() +{ + AUDIO_INFO_LOG("Start."); + needStart = 0; + std::unique_lock lock(statusLock_); + if (status_ != I_STATUS_IDLE && status_ != I_STATUS_PAUSED && status_ != I_STATUS_STOPPED) { + AUDIO_ERR_LOG("RendererInServer::Start failed, Illegal state: %{public}u", status_); + return ERR_ILLEGAL_STATE; + } + status_ = I_STATUS_STARTING; + int ret = stream_->Start(); + CHECK_AND_RETURN_RET_LOG(ret == SUCCESS, ret, "Start stream failed, reason: %{public}d", ret); + resetTime_ = true; + return SUCCESS; +} + +int32_t RendererInServer::Pause() +{ + AUDIO_INFO_LOG("Pause."); + std::unique_lock lock(statusLock_); + if (status_ != I_STATUS_STARTED) { + AUDIO_ERR_LOG("RendererInServer::Pause failed, Illegal state: %{public}u", status_); + return ERR_ILLEGAL_STATE; + } + status_ = I_STATUS_PAUSING; + int ret = stream_->Pause(); + CHECK_AND_RETURN_RET_LOG(ret == SUCCESS, ret, "Pause stream failed, reason: %{public}d", ret); + return SUCCESS; +} + +int32_t RendererInServer::Flush() +{ + AUDIO_INFO_LOG("Flush."); + std::unique_lock lock(statusLock_); + if (status_ == I_STATUS_STARTED) { + status_ = I_STATUS_FLUSHING_WHEN_STARTED; + } else if (status_ == I_STATUS_PAUSED) { + status_ = I_STATUS_FLUSHING_WHEN_PAUSED; + } else if (status_ == I_STATUS_STOPPED) { + status_ = I_STATUS_FLUSHING_WHEN_STOPPED; + } else { + AUDIO_ERR_LOG("RendererInServer::Flush failed, Illegal state: %{public}u", status_); + return ERR_ILLEGAL_STATE; + } + + // Flush buffer of audio server + uint64_t writeFrame = audioServerBuffer_->GetCurWriteFrame(); + uint64_t readFrame = audioServerBuffer_->GetCurReadFrame(); + + while (readFrame < writeFrame) { + BufferDesc bufferDesc = {nullptr, 0, 0}; + int32_t readResult = audioServerBuffer_->GetReadbuffer(readFrame, bufferDesc); + if (readResult != 0) { + return ERR_OPERATION_FAILED; + } + memset_s(bufferDesc.buffer, bufferDesc.bufLength, 0, bufferDesc.bufLength); + readFrame += spanSizeInFrame_; + AUDIO_INFO_LOG("On flush, read frame: %{public}" PRIu64 ", nextReadFrame: %{public}zu," + "writeFrame: %{public}" PRIu64 "", readFrame, spanSizeInFrame_, writeFrame); + audioServerBuffer_->SetCurReadFrame(readFrame); + } + + int ret = stream_->Flush(); + CHECK_AND_RETURN_RET_LOG(ret == SUCCESS, ret, "Flush stream failed, reason: %{public}d", ret); + return SUCCESS; +} + +int32_t RendererInServer::DrainAudioBuffer() +{ + return SUCCESS; +} + +int32_t RendererInServer::GetInfo() +{ + IStreamManager::GetPlaybackManager().GetInfo(); + return SUCCESS; +} + +int32_t RendererInServer::Drain() +{ + { + std::unique_lock lock(statusLock_); + if (status_ != I_STATUS_STARTED) { + AUDIO_ERR_LOG("RendererInServer::Drain failed, Illegal state: %{public}u", status_); + return ERR_ILLEGAL_STATE; + } + status_ = I_STATUS_DRAINING; + } + AUDIO_INFO_LOG("Start drain"); + DrainAudioBuffer(); + int ret = stream_->Drain(); + CHECK_AND_RETURN_RET_LOG(ret == SUCCESS, ret, "Drain stream failed, reason: %{public}d", ret); + return SUCCESS; +} + +int32_t RendererInServer::Stop() +{ + { + std::unique_lock lock(statusLock_); + if (status_ != I_STATUS_STARTED && status_ != I_STATUS_PAUSED) { + AUDIO_ERR_LOG("RendererInServer::Stop failed, Illegal state: %{public}u", status_); + return ERR_ILLEGAL_STATE; + } + status_ = I_STATUS_STOPPING; + } + int ret = stream_->Stop(); + CHECK_AND_RETURN_RET_LOG(ret == SUCCESS, ret, "Stop stream failed, reason: %{public}d", ret); + return SUCCESS; +} + +int32_t RendererInServer::Release() +{ + { + std::unique_lock lock(statusLock_); + CHECK_AND_RETURN_RET_LOG(status_ != I_STATUS_RELEASED && status_ != I_STATUS_IDLE, false, + "Illegal state: %{public}d", status_); + status_ = I_STATUS_RELEASED; + } + int32_t ret = IStreamManager::GetPlaybackManager().ReleaseRender(streamIndex_); + + stream_ = nullptr; + if (ret < 0) { + AUDIO_ERR_LOG("Release stream failed, reason: %{public}d", ret); + status_ = I_STATUS_INVALID; + return ret; + } + + return SUCCESS; +} + +int32_t RendererInServer::GetAudioTime(uint64_t &framePos, uint64_t &timeStamp) +{ + if (status_ == I_STATUS_STOPPED) { + AUDIO_WARNING_LOG("Current status is stopped"); + return ERR_ILLEGAL_STATE; + } + stream_->GetStreamFramesWritten(framePos); + stream_->GetCurrentTimeStamp(timeStamp); + if (resetTime_) { + resetTime_ = false; + resetTimestamp_ = timeStamp; + } + return SUCCESS; +} + +int32_t RendererInServer::GetLatency(uint64_t &latency) +{ + return stream_->GetLatency(latency); +} + +int32_t RendererInServer::SetRate(int32_t rate) +{ + return stream_->SetRate(rate); +} + +int32_t RendererInServer::SetLowPowerVolume(float volume) +{ + return stream_->SetLowPowerVolume(volume); +} + +int32_t RendererInServer::GetLowPowerVolume(float &volume) +{ + return stream_->GetLowPowerVolume(volume); +} + +int32_t RendererInServer::SetAudioEffectMode(int32_t effectMode) +{ + return stream_->SetAudioEffectMode(effectMode); +} + +int32_t RendererInServer::GetAudioEffectMode(int32_t &effectMode) +{ + return stream_->GetAudioEffectMode(effectMode); +} + +int32_t RendererInServer::SetPrivacyType(int32_t privacyType) +{ + return stream_->SetPrivacyType(privacyType); +} + +int32_t RendererInServer::GetPrivacyType(int32_t &privacyType) +{ + return stream_->GetPrivacyType(privacyType); +} + +int32_t RendererInServer::WriteOneFrame() +{ + // if buffer ready, get buffer, else, return + size_t minBufferSize = 0; + if (stream_->GetMinimumBufferSize(minBufferSize) < 0) { + AUDIO_ERR_LOG("Get min buffer size err"); + return ERR_OPERATION_FAILED; + } + BufferDesc bufferDesc = stream_->DequeueBuffer(minBufferSize); + stream_->EnqueueBuffer(bufferDesc); + return SUCCESS; +} + +std::shared_ptr RendererInServer::GetOHSharedBuffer() +{ + return audioServerBuffer_; +} +} // namespace AudioStandard +} // namespace OHOS diff --git a/services/audio_service/test/unittest/audio_service_common_unit_test.cpp b/services/audio_service/test/unittest/audio_service_common_unit_test.cpp index 2f88df7da8ff6cd15fea230a0f7a753a756cea66..a63a5256818ac72cf61ecfe64f497bc5054beabb 100644 --- a/services/audio_service/test/unittest/audio_service_common_unit_test.cpp +++ b/services/audio_service/test/unittest/audio_service_common_unit_test.cpp @@ -16,6 +16,7 @@ #include "audio_errors.h" #include "audio_log.h" #include "audio_info.h" +#include "audio_ring_cache.h" #include "linear_pos_time_model.h" #include "oh_audio_buffer.h" #include @@ -331,5 +332,333 @@ HWTEST(AudioServiceCommonUnitTest, OHAudioBuffer_008, TestSize.Level1) oHAudioBuffer = oHAudioBuffer->ReadFromParcel(parcel); EXPECT_NE(nullptr, oHAudioBuffer); } + +/** +* @tc.name : Test AudioRingCache API +* @tc.type : FUNC +* @tc.number: AudioRingCache_001 +* @tc.desc : Test AudioRingCache interface. +*/ +HWTEST(AudioServiceCommonUnitTest, AudioRingCache_001, TestSize.Level1) +{ + size_t testSize = 3840; + std::unique_ptr ringCache = AudioRingCache::Create(testSize); + EXPECT_NE(nullptr, ringCache); + + std::unique_ptr writeBuffer = std::make_unique(testSize); + std::unique_ptr readBuffer = std::make_unique(testSize); + + BufferWrap writeWrap = {writeBuffer.get(), testSize}; + BufferWrap readWrap = {readBuffer.get(), testSize}; + + int32_t tryCount = 200; + while (tryCount-- > 0) { + OptResult result1 = ringCache->Enqueue(writeWrap); + EXPECT_EQ(result1.ret, OPERATION_SUCCESS); + + OptResult result2 = ringCache->Dequeue(readWrap); + EXPECT_EQ(result2.ret, OPERATION_SUCCESS); + } +} + +/** +* @tc.name : Test AudioRingCache API +* @tc.type : FUNC +* @tc.number: AudioRingCache_002 +* @tc.desc : Test AudioRingCache interface. +*/ +HWTEST(AudioServiceCommonUnitTest, AudioRingCache_002, TestSize.Level1) +{ + size_t testSize = 3840; + std::unique_ptr ringCache = AudioRingCache::Create(testSize); + EXPECT_NE(nullptr, ringCache); + + size_t tempSize = 1920; + std::unique_ptr writeBuffer = std::make_unique(tempSize); + std::unique_ptr readBuffer = std::make_unique(tempSize); + + BufferWrap writeWrap = {writeBuffer.get(), tempSize}; + BufferWrap readWrap = {readBuffer.get(), tempSize}; + + int32_t tryCount = 200; + while (tryCount-- > 0) { + OptResult result1 = ringCache->GetWritableSize(); + EXPECT_EQ(result1.ret, OPERATION_SUCCESS); + EXPECT_EQ(result1.size, testSize); + + result1 = ringCache->Enqueue(writeWrap); // write 1920 + EXPECT_EQ(result1.ret, OPERATION_SUCCESS); + + result1 = ringCache->GetWritableSize(); + EXPECT_EQ(result1.ret, OPERATION_SUCCESS); + EXPECT_EQ(result1.size, testSize - tempSize); // left 1920 + + + OptResult result2 = ringCache->GetReadableSize(); + EXPECT_EQ(result2.ret, OPERATION_SUCCESS); + EXPECT_EQ(result2.size, tempSize); // can read 1920 + + result2 = ringCache->Dequeue(readWrap); + EXPECT_EQ(result2.ret, OPERATION_SUCCESS); + + result2 = ringCache->GetReadableSize(); + EXPECT_EQ(result2.ret, OPERATION_SUCCESS); + EXPECT_EQ(result2.size, 0); // can read 0 + } +} + +/** +* @tc.name : Test AudioRingCache API +* @tc.type : FUNC +* @tc.number: AudioRingCache_003 +* @tc.desc : Test AudioRingCache interface. +*/ +HWTEST(AudioServiceCommonUnitTest, AudioRingCache_003, TestSize.Level1) +{ + size_t cacheSize = 960; + std::unique_ptr ringCache = AudioRingCache::Create(cacheSize); + + size_t tempSize = 19200; + std::unique_ptr writeBuffer = std::make_unique(tempSize); + std::unique_ptr readBuffer = std::make_unique(tempSize); + + for (size_t index = 0; index < tempSize;index++) { + writeBuffer[index] = index; + } + + int32_t totalCount = tempSize / cacheSize; + size_t offset = 0; + while (totalCount-- > 0) { + uint8_t *writePtr = writeBuffer.get() + offset; + BufferWrap spanWrap = {writePtr, cacheSize}; + OptResult result1 = ringCache->Enqueue(spanWrap); + EXPECT_EQ(result1.ret, OPERATION_SUCCESS); + + uint8_t *readPtr = readBuffer.get() + offset; + BufferWrap readWrap = {readPtr, cacheSize}; + OptResult result2 = ringCache->Dequeue(readWrap); + EXPECT_EQ(result2.ret, OPERATION_SUCCESS); + offset += cacheSize; + } + + for (size_t index = 0; index < tempSize;index++) { + EXPECT_EQ(writeBuffer[index], readBuffer[index]); + } +} + +/** +* @tc.name : Test AudioRingCache API +* @tc.type : FUNC +* @tc.number: AudioRingCache_004 +* @tc.desc : Test AudioRingCache interface. +*/ +HWTEST(AudioServiceCommonUnitTest, AudioRingCache_004, TestSize.Level1) +{ + size_t cacheSize = 960; + std::unique_ptr ringCache = AudioRingCache::Create(cacheSize); + + size_t tempSize = 480; + std::unique_ptr writeBuffer = std::make_unique(tempSize); + std::unique_ptr readBuffer = std::make_unique(tempSize); + + for (size_t index = 0; index < tempSize;index++) { + writeBuffer[index] = index; + } + + BufferWrap writeWrap = {writeBuffer.get(), tempSize}; + BufferWrap readWrap = {readBuffer.get(), tempSize}; + + OptResult result1 = ringCache->Enqueue(writeWrap); + EXPECT_EQ(result1.ret, OPERATION_SUCCESS); + + result1 = ringCache->ReConfig(tempSize, true); // test copyRemained is true + EXPECT_EQ(result1.ret, OPERATION_SUCCESS); + EXPECT_EQ(result1.size, tempSize); + + result1 = ringCache->Dequeue(readWrap); + EXPECT_EQ(result1.ret, OPERATION_SUCCESS); + EXPECT_EQ(result1.size, tempSize); + + for (size_t index = 0; index < tempSize;index++) { + EXPECT_EQ(writeBuffer[index], readBuffer[index]); + } +} + +/** +* @tc.name : Test AudioRingCache API +* @tc.type : FUNC +* @tc.number: AudioRingCache_005 +* @tc.desc : Test AudioRingCache interface. +*/ +HWTEST(AudioServiceCommonUnitTest, AudioRingCache_005, TestSize.Level1) +{ + size_t cacheSize = 960; + std::unique_ptr ringCache = AudioRingCache::Create(cacheSize); + + size_t tempSize = 480; + std::unique_ptr writeBuffer = std::make_unique(tempSize); + std::unique_ptr readBuffer = std::make_unique(tempSize); + + for (size_t index = 0; index < tempSize;index++) { + writeBuffer[index] = index; + } + + BufferWrap writeWrap = {writeBuffer.get(), tempSize}; + BufferWrap readWrap = {readBuffer.get(), tempSize}; + + OptResult result1 = ringCache->Enqueue(writeWrap); + EXPECT_EQ(result1.ret, OPERATION_SUCCESS); + + result1 = ringCache->Dequeue(readWrap); + EXPECT_EQ(result1.ret, OPERATION_SUCCESS); + EXPECT_EQ(result1.size, tempSize); + + result1 = ringCache->Enqueue(writeWrap); + EXPECT_EQ(result1.ret, OPERATION_SUCCESS); + + result1 = ringCache->ReConfig(tempSize, true); // test copyRemained is true + EXPECT_EQ(result1.ret, OPERATION_SUCCESS); + EXPECT_EQ(result1.size, tempSize); + + result1 = ringCache->Dequeue(readWrap); + EXPECT_EQ(result1.ret, OPERATION_SUCCESS); + EXPECT_EQ(result1.size, tempSize); + + for (size_t index = 0; index < tempSize;index++) { + EXPECT_EQ(writeBuffer[index], readBuffer[index]); + } +} + +/** +* @tc.name : Test AudioRingCache API +* @tc.type : FUNC +* @tc.number: AudioRingCache_006 +* @tc.desc : Test AudioRingCache interface. +*/ +HWTEST(AudioServiceCommonUnitTest, AudioRingCache_006, TestSize.Level1) +{ + size_t cacheSize = 960; + std::unique_ptr ringCache = AudioRingCache::Create(cacheSize); + + size_t tempSize = 480; + std::unique_ptr writeBuffer = std::make_unique(tempSize); + + for (size_t index = 0; index < tempSize;index++) { + writeBuffer[index] = index; + } + + BufferWrap writeWrap = {writeBuffer.get(), tempSize}; + + OptResult result1 = ringCache->Enqueue(writeWrap); + EXPECT_EQ(result1.ret, OPERATION_SUCCESS); + + result1 = ringCache->Dequeue(writeWrap); + EXPECT_EQ(result1.ret, OPERATION_SUCCESS); + EXPECT_EQ(result1.size, tempSize); + + result1 = ringCache->Enqueue(writeWrap); + EXPECT_EQ(result1.ret, OPERATION_SUCCESS); + + result1 = ringCache->ReConfig(tempSize, false); // test copyRemained is false + EXPECT_EQ(result1.ret, OPERATION_SUCCESS); + EXPECT_EQ(result1.size, tempSize); + + result1 = ringCache->GetReadableSize(); + EXPECT_EQ(result1.ret, OPERATION_SUCCESS); + EXPECT_EQ(result1.size, 0); +} + +/** +* @tc.name : Test AudioRingCache API +* @tc.type : FUNC +* @tc.number: AudioRingCache_007 +* @tc.desc : Test AudioRingCache interface. +*/ +HWTEST(AudioServiceCommonUnitTest, AudioRingCache_007, TestSize.Level1) +{ + size_t cacheSize = 480; + std::unique_ptr ringCache = AudioRingCache::Create(cacheSize); + + size_t tempSize = 480; + std::unique_ptr writeBuffer = std::make_unique(tempSize); + + for (size_t index = 0; index < tempSize;index++) { + writeBuffer[index] = index; + } + + BufferWrap writeWrap = {writeBuffer.get(), tempSize}; + + OptResult result1 = ringCache->Enqueue(writeWrap); + EXPECT_EQ(result1.ret, OPERATION_SUCCESS); + + size_t reSize = tempSize + tempSize; + result1 = ringCache->ReConfig(reSize, false); // test copyRemained is false + EXPECT_EQ(result1.ret, OPERATION_SUCCESS); + EXPECT_EQ(result1.size, reSize); + + result1 = ringCache->GetReadableSize(); + EXPECT_EQ(result1.ret, OPERATION_SUCCESS); + EXPECT_EQ(result1.size, 0); + + result1 = ringCache->GetWritableSize(); + EXPECT_EQ(result1.ret, OPERATION_SUCCESS); + EXPECT_EQ(result1.size, reSize); + + result1 = ringCache->Enqueue(writeWrap); + EXPECT_EQ(result1.ret, OPERATION_SUCCESS); + EXPECT_EQ(result1.size, tempSize); + + result1 = ringCache->GetWritableSize(); + EXPECT_EQ(result1.ret, OPERATION_SUCCESS); + EXPECT_EQ(result1.size, tempSize); + + result1 = ringCache->Enqueue(writeWrap); + EXPECT_EQ(result1.ret, OPERATION_SUCCESS); + EXPECT_EQ(result1.size, tempSize); + + result1 = ringCache->GetWritableSize(); + EXPECT_EQ(result1.ret, OPERATION_SUCCESS); + EXPECT_EQ(result1.size, 0); +} + +/** +* @tc.name : Test AudioRingCache API +* @tc.type : FUNC +* @tc.number: AudioRingCache_008 +* @tc.desc : Test cross ring cache. +*/ +HWTEST(AudioServiceCommonUnitTest, AudioRingCache_008, TestSize.Level1) +{ + size_t cacheSize = 480; + std::unique_ptr ringCache = AudioRingCache::Create(cacheSize); + + size_t tempSize = 1920; + std::unique_ptr writeBuffer = std::make_unique(tempSize); + std::unique_ptr readBuffer = std::make_unique(tempSize); + + for (size_t index = 0; index < tempSize;index++) { + writeBuffer[index] = index % UINT8_MAX; + } + + size_t offset = 0; + size_t spanSize = 320; // 480 * 2 /3 + int32_t totalCount = tempSize / spanSize; + while (totalCount-- > 0) { + uint8_t *writePtr = writeBuffer.get() + offset; + BufferWrap spanWrap = {writePtr, spanSize}; + OptResult result1 = ringCache->Enqueue(spanWrap); + EXPECT_EQ(result1.ret, OPERATION_SUCCESS); + + uint8_t *readPtr = readBuffer.get() + offset; + BufferWrap readWrap = {readPtr, spanSize}; + OptResult result2 = ringCache->Dequeue(readWrap); + EXPECT_EQ(result2.ret, OPERATION_SUCCESS); + offset += spanSize; + } + + for (size_t index = 0; index < tempSize;index++) { + EXPECT_EQ(writeBuffer[index], readBuffer[index]); + } +} } // namespace AudioStandard } // namespace OHOS \ No newline at end of file diff --git a/test/BUILD.gn b/test/BUILD.gn index e363512f51a82ddd69554f00488b2113776e4d12..2ced24348a58adcfc89632376fe3c518d1113806 100644 --- a/test/BUILD.gn +++ b/test/BUILD.gn @@ -39,6 +39,7 @@ group("audio_unit_test") { "../frameworks/native/audiorenderer/test/unittest/renderer_test:audio_renderer_unit_test", "../frameworks/native/audiostream/test/unittest/stream_test:audio_stream_unit_test", "../frameworks/native/audioutils/test/unittest:audio_utils_unit_test", + "../frameworks/native/examples:pa_stream_test", "../frameworks/native/toneplayer/test/unnittest:audio_toneplayer_unit_test", "../services/audio_service/test/unittest:audio_balance_unit_test", ]