diff --git a/BUILD.gn b/BUILD.gn old mode 100755 new mode 100644 index f39feefe86afe40526820ef0db5e632249365a58..5d33a819a6b886fdce647fd2d05bb15d007b064d --- a/BUILD.gn +++ b/BUILD.gn @@ -16,6 +16,8 @@ import("//build/ohos.gni") group("audio_packages") { public_deps = [ "interfaces/innerkits/native/audiocapturer:audio_capturer", + "interfaces/innerkits/native/audiorenderer:audio_renderer", + "interfaces/innerkits/native/audiorecorder:audio_recorder", "services:audio_service", "interfaces/innerkits/native/audiomanager:audio_client", ] diff --git a/README.md b/README.md old mode 100755 new mode 100644 index 80fd4977b588979a7de87b31c76caa4d14ae75e6..660d5e8b010d540a1e64b8bcfe677c7134438f61 --- a/README.md +++ b/README.md @@ -1,22 +1,23 @@ # Audio -- [Introduction](#section119mcpsimp) - - [Basic Concepts](#section122mcpsimp) - -- [Directory Structure](#section179mcpsimp) -- [Usage Guidelines](#section112738505318) -- [Repositories Involved](#section340mcpsimp) - -## Introduction - -The **audio\_standard** repository supports the development of audio services. You can use this module to manage audio volume. + - [Introduction](#introduction) + - [Basic Concepts](#basic-concepts) + - [Directory Structure](#directory-structure) + - [Usage Guidelines](#usage-guidelines) + - [Audio Playback](#audio-playback) + - [Audio Recording](#audio-recording) + - [Audio Management](#audio-management) + - [Repositories Involved](#repositories-involved) + +## Introduction +The **audio\_standard** repository is used to implement audio-related features, including audio playback, recording, volume management and device management. **Figure 1** Position in the subsystem architecture ![](figures/en-us_image_0000001152315135.png) -### Basic Concepts +### Basic Concepts - **Sampling** @@ -24,7 +25,7 @@ Sampling is a process to obtain discrete-time signals by extracting samples from - **Sampling rate** -Sampling rate is the number of samples extracted from a continuous signal per second to form a discrete signal. It is measured in Hz. Generally, human hearing range is from 20 Hz to 20 kHz. Common audio sampling rates include 8 kHz, 11.025 kHz, 22.05 kHz, 16 kHz, 37.8 kHz, 44.1 kHz, 48 kHz, 96 kHz, and 192 kHz. +Sampling rate is the number of samples extracted from a continuous signal per second to form a discrete signal. It is measured in Hz. Generally, human hearing range is from 20 Hz to 20 kHz. Common audio sampling rates include 8 kHz, 11.025 kHz, 22.05 kHz, 16 kHz, 37.8 kHz, 44.1 kHz, 48 kHz, and 96 kHz. - **Channel** @@ -38,58 +39,117 @@ Audio data is in stream form. For the convenience of audio algorithm processing Pulse code modulation \(PCM\) is a method used to digitally represent sampled analog signals. It converts continuous-time analog signals into discrete-time digital signal samples. -## Directory Structure +## Directory Structure The structure of the repository directory is as follows: ``` /foundation/multimedia/audio_standard # Audio code ├── frameworks # Framework code -│ ├── innerkitsimpl # Internal interfaces implementation -│ └── kitsimpl # External interfaces implementation -├── interfaces # Interfaces code -│ ├── innerkits # Internal interfaces -│ └── kits # External interfaces +│ ├── innerkitsimpl # Internal Native API Implementation. +| | Pulseaudio, libsndfile build configuration and pulseaudio-hdi modules +│ └── kitsimpl # External JS API Implementation +├── interfaces # Interfaces +│ ├── innerkits # Internal Native APIs +│ └── kits # External JS APIs ├── sa_profile # Service configuration profile ├── services # Service code ├── LICENSE # License file └── ohos.build # Build file ``` -## Usage Guidelines - -1. Obtain an **AudioManager** instance. +## Usage Guidelines +### Audio Playback +You can use APIs provided in this repository to convert audio data into audible analog signals, play the audio signals using output devices, and manage playback tasks. The following steps describe how to use **AudioRenderer** to develop the audio playback function: +1. Use **Create** API with required stream type to get **AudioRenderer** instance. + ``` + AudioStreamType streamType = STREAM_MUSIC; // example stream type + std::unique_ptr audioRenderer = AudioRenderer::Create(streamType); + ``` +2. (Optional) Static APIs **GetSupportedFormats**(), **GetSupportedChannels**(), **GetSupportedEncodingTypes**(), **GetSupportedSamplingRates**() can be used to get the supported values of the params. +3. To Prepare the device, call **SetParams** on the instance. + ``` + AudioRendererParams rendererParams; + rendererParams.sampleFormat = SAMPLE_S16LE; + rendererParams.sampleRate = SAMPLE_RATE_44100; + rendererParams.channelCount = STEREO; + rendererParams.encodingType = ENCODING_PCM; + audioRenderer->SetParams(rendererParams); + ``` +4. (Optional) use audioRenderer->**GetParams**(rendererParams) to validate SetParams +5. Call **audioRenderer->Start()** function on the AudioRenderer instance to start the playback task. +6. Get the buffer length to be written, using **GetBufferSize** API . + ``` + audioRenderer->GetBufferSize(bufferLen); + ``` +7. Read the audio data to be played from the source(for example, an audio file) and transfer it into the bytes stream. Call the **Write** function repeatedly to write the render data. ``` - const audioManager = audio.getAudioManager(); + bytesToWrite = fread(buffer, 1, bufferLen, wavFile); + while ((bytesWritten < bytesToWrite) && ((bytesToWrite - bytesWritten) > minBytes)) { + bytesWritten += audioRenderer->Write(buffer + bytesWritten, bytesToWrite - bytesWritten); + if (bytesWritten < 0) + break; + } ``` +8. Call audioRenderer->**Drain**() to drain the playback stream. -2. Obtain the audio stream volume. +9. Call audioRenderer->**Stop()** function to Stop rendering. +10. After the playback task is complete, call the audioRenderer->**Release**() function on the AudioRenderer instance to release the resources. +Provided the basic playback usecase above. Please refer [**audio_renderer.h**](https://gitee.com/openharmony/multimedia_audio_standard/blob/master/interfaces/innerkits/native/audiorenderer/include/audio_renderer.h) and [**audio_info.h**](https://gitee.com/openharmony/multimedia_audio_standard/blob/master/interfaces/innerkits/native/audiocommon/include/audio_info.h) for more APIs. + + +### Audio Recording +You can use the APIs provided in this repository for your application to record voices using input devices, convert the voices into audio data, and manage recording tasks. The following steps describe how to use AudioReorder to develop the audio recording function: + +1. Use **Create** API with required stream type to get **AudioRecorder** instance. ``` - audioManager.getVolume(audio.AudioVolumeType.MEDIA, (err, value) => { - if (err) { - console.error(`failed to get volume ${err.message}`); - return; - } - console.log(`Media getVolume successful callback`); - }); + AudioStreamType streamType = STREAM_MUSIC; + std::unique_ptr audioRecorder = AudioRecorder::Create(streamType); ``` +2. (Optional) Static APIs **GetSupportedFormats**(), **GetSupportedChannels**(), **GetSupportedEncodingTypes**(), **GetSupportedSamplingRates()** can be used to get the supported values of the params. +3. To Prepare the device, call **SetParams** on the instance. + ``` + AudioRecorderParams recorderParams; + recorderParams.sampleFormat = SAMPLE_S16LE; + recorderParams.sampleRate = SAMPLE_RATE_44100; + recorderParams.channelCount = STEREO; + recorderParams.encodingType = ENCODING_PCM; -3. Set the audio stream volume. - + audioRecorder->SetParams(recorderParams); ``` - audioManager.setVolume(audio.AudioVolumeType.MEDIA, 30, (err)=>{ - if (err) { - console.error(`failed to set volume ${err.message}`); - return; - } - console.log(`Media setVolume successful callback`); - }) +4. (Optional) use audioRecorder->**GetParams**(recorderParams) to validate SetParams() +5. Call audioRenderer->**Start**() function on the AudioRecorder instance to start the recording task. +6. Get the buffer length to be read, using **GetBufferSize** API. ``` + audioRecorder->GetBufferSize(bufferLen); + ``` +7. Read the recorded audio data and convert it to a byte stream. Call the read function repeatedly to read data untill you want to stop recording + ``` + bytesRead = audioRecorder->Read(*buffer, bufferLen, isBlocking); // set isBlocking = true/false for blocking/non-blocking read + while (numBuffersToRecord) { + bytesRead = audioRecorder->Read(*buffer, bufferLen, isBlockingRead); + if (bytesRead < 0) { + break; + } else if (bytesRead > 0) { + fwrite(buffer, size, bytesRead, recFile); // example shows writes the recored data into a file + numBuffersToRecord--; + } + } + ``` +8. (Optional) Call audioRecorder->**Flush**() to flush the record buffer of this stream. +9. Call the audioRecorder->**Stop**() function on the AudioRecorder instance to stop the recording. +10. After the recording task is complete, call the audioRecorder->**Release**() function on the AudioRecorder instance to release resources. + +Provided the basic recording usecase above. Please refer [**audio_recorder.h**](https://gitee.com/openharmony/multimedia_audio_standard/blob/master/interfaces/innerkits/native/audiorecorder/include/audio_recorder.h) and [**audio_info.h**](https://gitee.com/openharmony/multimedia_audio_standard/blob/master/interfaces/innerkits/native/audiocommon/include/audio_info.h) for more APIs. +### Audio Management -## Repositories Involved +JS apps can use the APIs provided by audio manager to control the volume and the device.\ +Please refer [**audio-management.md**](https://gitee.com/openharmony/docs/blob/master/en/application-dev/js-reference/audio-management.md) for JS usage of audio volume and device management. -multimedia\_audio\_standard +## Repositories Involved +[multimedia\_audio\_standard](https://gitee.com/openharmony/multimedia_audio_standard)\ +[multimedia\_media\_standard](https://gitee.com/openharmony/multimedia_media_standard) diff --git a/frameworks/innerkitsimpl/audiocapturer/BUILD.gn b/frameworks/innerkitsimpl/audiocapturer/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..2b73a0d4119bcad577a686b9174ce74c8f9c3b73 --- /dev/null +++ b/frameworks/innerkitsimpl/audiocapturer/BUILD.gn @@ -0,0 +1,57 @@ +# Copyright (C) 2021 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("//drivers/adapter/uhdf2/uhdf.gni") + +ohos_shared_library("audio_capturer_source") { + install_enable = true + sources = [ + "//foundation/multimedia/audio_standard/frameworks/innerkitsimpl/audiocapturer/src/audio_capturer_source.cpp", + ] + cflags = [ "-fPIC" ] + cflags += [ "-Wall" ] + cflags_cc = cflags + + include_dirs = [ + "//foundation/multimedia/audio_standard/frameworks/innerkitsimpl/audiocapturer/include", + "//foundation/multimedia/audio_standard/frameworks/innerkitsimpl/common/include", + "//foundation/multimedia/media_standard/interfaces/innerkits/native/media/include", + "//utils/native/base/include", + "//base/hiviewdfx/hilog/interfaces/native/innerkits/include", + "//drivers/peripheral/audio/interfaces/include", + "//third_party/bounds_checking_function/include", + ] + public_configs = [ ":audio_external_library_config" ] + + deps = [ + "//base/hiviewdfx/hilog/interfaces/native/innerkits:libhilog", + "//third_party/bounds_checking_function:libsec_static", + "$hdf_uhdf_path/config:libhdf_hcs", + "$hdf_uhdf_path/hcs:hdf_default.hcb", + "$hdf_uhdf_path/hdi:libhdi", + "$hdf_uhdf_path/host:hdf_devhost", + "$hdf_uhdf_path/host:libhdf_host", + "$hdf_uhdf_path/ipc:libhdf_ipc_adapter", + "$hdf_uhdf_path/manager:hdf_devmgr", + "$hdf_uhdf_path/manager:hdf_devmgr.rc", + "$hdf_uhdf_path/osal:libhdf_utils", + ] + + part_name = "multimedia_audio_standard" + subsystem_name = "multimedia" +} + +config("audio_external_library_config") { + include_dirs = ["//foundation/multimedia/audio_standard/frameworks/innerkitsimpl/common/include"] +} diff --git a/frameworks/innerkitsimpl/audiocapturer/include/audio_capturer_source.h b/frameworks/innerkitsimpl/audiocapturer/include/audio_capturer_source.h new file mode 100644 index 0000000000000000000000000000000000000000..d2e675cdea3ac1a3a07fdab4eddccd87bc107794 --- /dev/null +++ b/frameworks/innerkitsimpl/audiocapturer/include/audio_capturer_source.h @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2021 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_CAPTURER_SOURCE_H +#define AUDIO_CAPTURER_SOURCE_H + +#include "audio_manager.h" + +#include + +namespace OHOS { +namespace AudioStandard { +#define AUDIO_CHANNELCOUNT 2 +#define AUDIO_SAMPLE_RATE_48K 48000 +#define DEEP_BUFFER_CAPTURE_PERIOD_SIZE 4096 +#define INT_32_MAX 0x7fffffff +#define PERIOD_SIZE 1024 +#define PATH_LEN 256 +#define AUDIO_BUFF_SIZE (16 * 1024) +#define PCM_8_BIT 8 +#define PCM_16_BIT 16 + +typedef struct { + AudioFormat format; + uint32_t sampleFmt; + uint32_t sampleRate; + uint32_t channel; + float volume; +} AudioSourceAttr; + +class AudioCapturerSource { +public: + int32_t Init(AudioSourceAttr &atrr); + void DeInit(void); + + int32_t Start(void); + int32_t Stop(void); + int32_t Flush(void); + int32_t Reset(void); + int32_t Pause(void); + int32_t Resume(void); + int32_t CaptureFrame(char *frame, uint64_t requestBytes, uint64_t &replyBytes); + int32_t SetVolume(float left, float right); + int32_t GetVolume(float &left, float &right); + int32_t SetMute(bool isMute); + int32_t GetMute(bool &isMute); + + static AudioCapturerSource* GetInstance(void); + bool capturerInited_; + +private: + const int32_t HALF_FACTOR = 2; + const int32_t MAX_AUDIO_ADAPTER_NUM = 3; + const float MAX_VOLUME_LEVEL = 15.0f; + + AudioSourceAttr attr_; + bool started_; + bool paused_; + float leftVolume_; + float rightVolume_; + + struct AudioManager *audioManager_; + struct AudioAdapter *audioAdapter_; + struct AudioCapture *audioCapture_; + + void *handle_; + + int32_t CreateCapture(struct AudioPort &capturePort); + int32_t InitAudioManager(); + +#ifdef CAPTURE_DUMP + FILE *pfd; +#endif + + AudioCapturerSource(); + ~AudioCapturerSource(); +}; +} // namespace AudioStandard +} // namespace OHOS +#endif // AUDIO_CAPTURER_SOURCE_H diff --git a/frameworks/innerkitsimpl/audiocapturer/include/audio_capturer_source_intf.h b/frameworks/innerkitsimpl/audiocapturer/include/audio_capturer_source_intf.h new file mode 100644 index 0000000000000000000000000000000000000000..a7e89c3617bdfcf56f1cca7bbd42e81b66844342 --- /dev/null +++ b/frameworks/innerkitsimpl/audiocapturer/include/audio_capturer_source_intf.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2021 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_CAPTURER_SINK_INTF_H +#define AUDIO_CAPTURER_SINK_INTF_H + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + enum AudioFormat format; + uint32_t sampleFmt; + uint32_t sampleRate; + uint32_t channel; + float volume; +} AudioSourceAttr; + +int32_t AudioCapturerSourceInit(AudioSourceAttr *); +void AudioCapturerSourceDeInit(void); +int32_t AudioCapturerSourceStart(void); +int32_t AudioCapturerSourceStop(void); +int32_t AudioCapturerSourceFrame(char *, uint64_t, uint64_t *); +int32_t AudioCapturerSourceSetVolume(float, float); +int32_t AudioCapturerSourceGetVolume(float *left, float *right); +#ifdef __cplusplus +} +#endif + +#endif // AUDIO_CAPTURER_SINK_INTF_H \ No newline at end of file diff --git a/frameworks/innerkitsimpl/audiocapturer/include/audio_encoder.h b/frameworks/innerkitsimpl/audiocapturer/include/audio_encoder.h index 68afee6392b2d59950d96a2eaa697b186c71a5ab..4c0a2b832eb00bb8d0e4c77e433c1e844f406e00 100755 --- a/frameworks/innerkitsimpl/audiocapturer/include/audio_encoder.h +++ b/frameworks/innerkitsimpl/audiocapturer/include/audio_encoder.h @@ -21,7 +21,7 @@ #include #include #include -#include "media_errors.h" +#include "audio_errors.h" #include "media_info.h" #include "format.h" #include "codec_interface.h" diff --git a/frameworks/innerkitsimpl/audiocapturer/include/audio_source.h b/frameworks/innerkitsimpl/audiocapturer/include/audio_source.h index 1b03fa5a5a2eb0b1b75ba26f13e216607bdd9d6c..a540a0542be5b9ab715400ef2b8d6cde2d2c690d 100755 --- a/frameworks/innerkitsimpl/audiocapturer/include/audio_source.h +++ b/frameworks/innerkitsimpl/audiocapturer/include/audio_source.h @@ -22,7 +22,7 @@ #include #include -#include "media_errors.h" +#include "audio_errors.h" #include "media_info.h" #include "format.h" #include "audio_manager.h" diff --git a/frameworks/innerkitsimpl/audiocapturer/src/audio_capturer_impl.cpp b/frameworks/innerkitsimpl/audiocapturer/src/audio_capturer_impl.cpp index 902047351b389feb6c2b4493e9008b70f2f79d50..7035b166b9477c9190d912f8ae2f64c6938681b1 100755 --- a/frameworks/innerkitsimpl/audiocapturer/src/audio_capturer_impl.cpp +++ b/frameworks/innerkitsimpl/audiocapturer/src/audio_capturer_impl.cpp @@ -23,6 +23,7 @@ namespace OHOS { namespace Audio { using namespace OHOS::Media; +using namespace OHOS::AudioStandard; const unsigned long long TIME_CONVERSION_US_S = 1000000ULL; /* us to s */ const unsigned long long TIME_CONVERSION_NS_US = 1000ULL; /* ns to us */ diff --git a/frameworks/innerkitsimpl/audiocapturer/src/audio_capturer_source.cpp b/frameworks/innerkitsimpl/audiocapturer/src/audio_capturer_source.cpp new file mode 100644 index 0000000000000000000000000000000000000000..915f97e9e0a914dee32620010734409fed65b025 --- /dev/null +++ b/frameworks/innerkitsimpl/audiocapturer/src/audio_capturer_source.cpp @@ -0,0 +1,490 @@ +/* + * Copyright (C) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include "audio_capturer_source.h" +#include "audio_errors.h" +#include "media_log.h" + +namespace OHOS { +namespace AudioStandard { +#ifdef CAPTURE_DUMP +const char *g_audioOutTestFilePath = "/data/local/tmp/audio_capture.pcm"; +#endif // CAPTURE_DUMP + +AudioCapturerSource::AudioCapturerSource() + : capturerInited_(false), started_(false), paused_(false), + leftVolume_(MAX_VOLUME_LEVEL), rightVolume_(MAX_VOLUME_LEVEL), + audioManager_(nullptr), audioAdapter_(nullptr), audioCapture_(nullptr) +{ + attr_ = {}; +#ifdef CAPTURE_DUMP + pfd = nullptr; +#endif // CAPTURE_DUMP +} + +AudioCapturerSource::~AudioCapturerSource() +{ + DeInit(); +} + +AudioCapturerSource* AudioCapturerSource::GetInstance() +{ + static AudioCapturerSource audioCapturer_; + return &audioCapturer_; +} + +void AudioCapturerSource::DeInit() +{ + started_ = false; + capturerInited_ = false; + + if ((audioCapture_ != nullptr) && (audioAdapter_ != nullptr)) { + audioAdapter_->DestroyCapture(audioAdapter_, audioCapture_); + } + audioCapture_ = nullptr; + + if ((audioManager_ != nullptr) && (audioAdapter_ != nullptr)) { + audioManager_->UnloadAdapter(audioManager_, audioAdapter_); + } + audioAdapter_ = nullptr; + audioManager_ = nullptr; + dlclose(handle_); +#ifdef CAPTURE_DUMP + if (pfd) { + fclose(pfd); + pfd = nullptr; + } +#endif // CAPTURE_DUMP +} + +int32_t InitAttrsCapture(struct AudioSampleAttributes &attrs) +{ + /* Initialization of audio parameters for playback */ + attrs.format = AUDIO_FORMAT_PCM_16_BIT; + attrs.channelCount = AUDIO_CHANNELCOUNT; + attrs.sampleRate = AUDIO_SAMPLE_RATE_48K; + attrs.interleaved = 0; + attrs.type = AUDIO_IN_MEDIA; + attrs.period = DEEP_BUFFER_CAPTURE_PERIOD_SIZE; + attrs.frameSize = PCM_16_BIT * attrs.channelCount / PCM_8_BIT; + attrs.isBigEndian = false; + attrs.isSignedData = true; + attrs.startThreshold = DEEP_BUFFER_CAPTURE_PERIOD_SIZE / (attrs.frameSize); + attrs.stopThreshold = INT_32_MAX; + /* 16 * 1024 */ + attrs.silenceThreshold = AUDIO_BUFF_SIZE; + + return SUCCESS; +} + +int32_t SwitchAdapterCapture(struct AudioAdapterDescriptor *descs, const char *adapterNameCase, + enum AudioPortDirection portFlag, struct AudioPort *capturePort, const int32_t size) +{ + for (int32_t index = 0; index < size; index++) { + struct AudioAdapterDescriptor *desc = &descs[index]; + if (desc == nullptr) { + MEDIA_ERR_LOG("SwitchAdapter Fail AudioAdapterDescriptor desc NULL"); + return ERR_INVALID_INDEX; // note: return invalid index not invalid handle + } + if (!strcmp(desc->adapterName, adapterNameCase)) { + for (uint32_t port = 0; ((desc != NULL) && (port < desc->portNum)); port++) { + // Only find out the port of out in the sound card + if (desc->ports[port].dir == portFlag) { + *capturePort = desc->ports[port]; + return index; + } + } + } + } + MEDIA_ERR_LOG("SwitchAdapter Fail"); + + return ERR_INVALID_INDEX; +} + +int32_t AudioCapturerSource::InitAudioManager() +{ + char resolvedPath[100] = "/system/lib/libhdi_audio.z.so"; + struct AudioManager *(*getAudioManager)() = nullptr; + + handle_ = dlopen(resolvedPath, 1); + if (handle_ == nullptr) { + MEDIA_ERR_LOG("Open Capturer so Fail"); + return ERR_INVALID_HANDLE; + } + + getAudioManager = (struct AudioManager *(*)())(dlsym(handle_, "GetAudioManagerFuncs")); + audioManager_ = getAudioManager(); + if (audioManager_ == nullptr) { + return ERR_INVALID_HANDLE; + } + + return 0; +} + +int32_t AudioCapturerSource::CreateCapture(struct AudioPort &capturePort) +{ + // Initialization port information, can fill through mode and other parameters + int32_t ret = audioAdapter_->InitAllPorts(audioAdapter_); + if (ret != 0) { + MEDIA_ERR_LOG("InitAllPorts failed"); + return ERR_DEVICE_INIT; + } + + struct AudioSampleAttributes param; + // User needs to set + InitAttrsCapture(param); + param.sampleRate = attr_.sampleRate; + param.format = attr_.format; + param.channelCount = attr_.channel; + + struct AudioDeviceDescriptor deviceDesc; + deviceDesc.portId = capturePort.portId; + deviceDesc.pins = PIN_IN_MIC; + deviceDesc.desc = nullptr; + + ret = audioAdapter_->CreateCapture(audioAdapter_, &deviceDesc, ¶m, &audioCapture_); + if (audioCapture_ == nullptr || ret < 0) { + MEDIA_ERR_LOG("Create capture failed"); + return ERR_NOT_STARTED; + } + + capturerInited_ = true; + + return 0; +} + +int32_t AudioCapturerSource::Init(AudioSourceAttr &attr) +{ + attr_ = attr; + int32_t ret; + int32_t index; + int32_t size = 0; + struct AudioAdapterDescriptor *descs = nullptr; + struct AudioPort capturePort; + + if (InitAudioManager() != 0) { + MEDIA_ERR_LOG("Init audio manager Fail"); + return ERR_INVALID_HANDLE; + } + + ret = audioManager_->GetAllAdapters(audioManager_, &descs, &size); + // adapters is 0~3 + if (size > MAX_AUDIO_ADAPTER_NUM || size == 0 || descs == nullptr || ret < 0) { + MEDIA_ERR_LOG("Get adapters Fail"); + return ERR_NOT_STARTED; + } + + // Get qualified sound card and port + char adapterNameCase[PATH_LEN] = "internal"; + index = SwitchAdapterCapture(descs, adapterNameCase, PORT_IN, &capturePort, size); + if (index < 0) { + MEDIA_ERR_LOG("Switch Adapter Fail"); + return ERR_NOT_STARTED; + } + + struct AudioAdapterDescriptor *desc = &descs[index]; + if (audioManager_->LoadAdapter(audioManager_, desc, &audioAdapter_) != 0) { + MEDIA_ERR_LOG("Load Adapter Fail"); + return ERR_NOT_STARTED; + } + if (audioAdapter_ == nullptr) { + MEDIA_ERR_LOG("Load audio device failed"); + return ERR_NOT_STARTED; + } + + if (CreateCapture(capturePort) != 0) { + MEDIA_ERR_LOG("Create capture failed"); + return ERR_NOT_STARTED; + } +#ifdef CAPTURE_DUMP + pfd = fopen(g_audioOutTestFilePath, "wb+"); + if (pfd == nullptr) { + MEDIA_ERR_LOG("Error opening pcm test file!"); + } +#endif // CAPTURE_DUMP + + return SUCCESS; +} + +int32_t AudioCapturerSource::CaptureFrame(char *frame, uint64_t requestBytes, uint64_t &replyBytes) +{ + int32_t ret; + if (audioCapture_ == nullptr) { + MEDIA_ERR_LOG("Audio capture Handle is nullptr!"); + return ERR_INVALID_HANDLE; + } + + ret = audioCapture_->CaptureFrame(audioCapture_, frame, requestBytes, &replyBytes); + if (ret < 0) { + MEDIA_ERR_LOG("Capture Frame Fail"); + return ERR_READ_FAILED; + } + +#ifdef CAPTURE_DUMP + size_t writeResult = fwrite(frame, replyBytes, 1, pfd); + if (writeResult != replyBytes) { + MEDIA_ERR_LOG("Failed to write the file."); + } +#endif // CAPTURE_DUMP + + return SUCCESS; +} + +int32_t AudioCapturerSource::Start(void) +{ + int32_t ret; + if (!started_ && audioCapture_ != nullptr) { + ret = audioCapture_->control.Start((AudioHandle)audioCapture_); + if (ret < 0) { + return ERR_NOT_STARTED; + } + started_ = true; + audioCapture_->volume.SetVolume(reinterpret_cast(audioCapture_), MAX_VOLUME_LEVEL); + } + + return SUCCESS; +} + +int32_t AudioCapturerSource::SetVolume(float left, float right) +{ + float volume; + if (audioCapture_ == nullptr) { + MEDIA_ERR_LOG("AudioCapturerSource::SetVolume failed audioCapture_ null"); + return ERR_INVALID_HANDLE; + } + + leftVolume_ = left; + rightVolume_ = right; + if ((leftVolume_ == 0) && (rightVolume_ != 0)) { + volume = rightVolume_; + } else if ((leftVolume_ != 0) && (rightVolume_ == 0)) { + volume = leftVolume_; + } else { + volume = (leftVolume_ + rightVolume_) / HALF_FACTOR; + } + + audioCapture_->volume.SetVolume(reinterpret_cast(audioCapture_), volume); + + return SUCCESS; +} + +int32_t AudioCapturerSource::GetVolume(float &left, float &right) +{ + float val = 0.0; + audioCapture_->volume.GetVolume((AudioHandle)audioCapture_, &val); + left = val; + right = val; + + return SUCCESS; +} + +int32_t AudioCapturerSource::SetMute(bool isMute) +{ + int32_t ret; + if (audioCapture_ == nullptr) { + MEDIA_ERR_LOG("AudioCapturerSource::SetMute failed audioCapture_ handle is null!"); + return ERR_INVALID_HANDLE; + } + + ret = audioCapture_->volume.SetMute((AudioHandle)audioCapture_, isMute); + if (ret != 0) { + MEDIA_ERR_LOG("AudioCapturerSource::SetMute failed"); + return ERR_OPERATION_FAILED; + } + + return SUCCESS; +} + +int32_t AudioCapturerSource::GetMute(bool &isMute) +{ + int32_t ret; + if (audioCapture_ == nullptr) { + MEDIA_ERR_LOG("AudioCapturerSource::GetMute failed audioCapture_ handle is null!"); + return ERR_INVALID_HANDLE; + } + + ret = audioCapture_->volume.GetMute((AudioHandle)audioCapture_, &isMute); + if (ret != 0) { + MEDIA_ERR_LOG("AudioCapturerSource::GetMute failed"); + return ERR_OPERATION_FAILED; + } + + return SUCCESS; +} + +int32_t AudioCapturerSource::Stop(void) +{ + int32_t ret; + if (started_ && audioCapture_ != nullptr) { + ret = audioCapture_->control.Stop(reinterpret_cast(audioCapture_)); + if (ret < 0) { + MEDIA_ERR_LOG("Stop capture Failed"); + return ERR_OPERATION_FAILED; + } + } + started_ = false; + + return SUCCESS; +} + +int32_t AudioCapturerSource::Pause(void) +{ + int32_t ret; + if (started_ && audioCapture_ != nullptr) { + ret = audioCapture_->control.Pause(reinterpret_cast(audioCapture_)); + if (ret != 0) { + MEDIA_ERR_LOG("pause capture Failed"); + return ERR_OPERATION_FAILED; + } + } + paused_ = true; + + return SUCCESS; +} + +int32_t AudioCapturerSource::Resume(void) +{ + int32_t ret; + if (paused_ && audioCapture_ != nullptr) { + ret = audioCapture_->control.Resume(reinterpret_cast(audioCapture_)); + if (ret != 0) { + MEDIA_ERR_LOG("resume capture Failed"); + return ERR_OPERATION_FAILED; + } + } + paused_ = false; + + return SUCCESS; +} + +int32_t AudioCapturerSource::Reset(void) +{ + if (started_ && audioCapture_ != nullptr) { + audioCapture_->control.Flush(reinterpret_cast(audioCapture_)); + } + + return SUCCESS; +} + +int32_t AudioCapturerSource::Flush(void) +{ + if (started_ && audioCapture_ != nullptr) { + audioCapture_->control.Flush(reinterpret_cast(audioCapture_)); + } + + return SUCCESS; +} +} // namespace AudioStandard +} // namesapce OHOS + +#ifdef __cplusplus +extern "C" { +#endif + +using namespace OHOS::AudioStandard; + +AudioCapturerSource* g_audioCaptureSourceInstance = AudioCapturerSource::GetInstance(); + +int32_t AudioCapturerSourceInit(AudioSourceAttr *attr) +{ + int32_t ret; + + if (g_audioCaptureSourceInstance->capturerInited_) + return SUCCESS; + + ret = g_audioCaptureSourceInstance->Init(*attr); + + return ret; +} + +void AudioCapturerSourceDeInit() +{ + if (g_audioCaptureSourceInstance->capturerInited_) + g_audioCaptureSourceInstance->DeInit(); +} + +int32_t AudioCapturerSourceStop() +{ + int32_t ret; + + if (!g_audioCaptureSourceInstance->capturerInited_) + return SUCCESS; + + ret = g_audioCaptureSourceInstance->Stop(); + + return ret; +} + +int32_t AudioCapturerSourceStart() +{ + int32_t ret; + + if (!g_audioCaptureSourceInstance->capturerInited_) { + MEDIA_ERR_LOG("audioCapturer Not Inited! Init the capturer first\n"); + return ERR_DEVICE_INIT; + } + + ret = g_audioCaptureSourceInstance->Start(); + + return ret; +} + +int32_t AudioCapturerSourceFrame(char *frame, uint64_t requestBytes, uint64_t &replyBytes) +{ + int32_t ret; + + if (!g_audioCaptureSourceInstance->capturerInited_) { + MEDIA_ERR_LOG("audioCapturer Not Inited! Init the capturer first\n"); + return ERR_DEVICE_INIT; + } + + ret = g_audioCaptureSourceInstance->CaptureFrame(frame, requestBytes, replyBytes); + + return ret; +} + +int32_t AudioCapturerSourceSetVolume(float left, float right) +{ + int32_t ret; + + if (!g_audioCaptureSourceInstance->capturerInited_) { + MEDIA_ERR_LOG("audioCapturer Not Inited! Init the capturer first\n"); + return ERR_DEVICE_INIT; + } + + ret = g_audioCaptureSourceInstance->SetVolume(left, right); + + return ret; +} + +int32_t AudioCapturerSourceGetVolume(float *left, float *right) +{ + int32_t ret; + + if (!g_audioCaptureSourceInstance->capturerInited_) { + MEDIA_ERR_LOG("audioCapturer Not Inited! Init the capturer first\n"); + return ERR_DEVICE_INIT; + } + ret = g_audioCaptureSourceInstance->GetVolume(*left, *right); + + return ret; +} + +#ifdef __cplusplus +} +#endif diff --git a/frameworks/innerkitsimpl/audiocapturer/src/audio_encoder.cpp b/frameworks/innerkitsimpl/audiocapturer/src/audio_encoder.cpp index e84726f2cd900158afd40bb0bc5aa95ce035e7a6..fa9fed58ed7c88117f16eba36853cf0af7fd6c50 100755 --- a/frameworks/innerkitsimpl/audiocapturer/src/audio_encoder.cpp +++ b/frameworks/innerkitsimpl/audiocapturer/src/audio_encoder.cpp @@ -20,6 +20,7 @@ namespace OHOS { namespace Audio { using namespace OHOS::Media; +using namespace OHOS::AudioStandard; constexpr uint32_t AUDIO_READ_STREAM_TIME_OUT_MS = 1000; /* 1S */ diff --git a/frameworks/innerkitsimpl/audiocapturer/src/audio_source.cpp b/frameworks/innerkitsimpl/audiocapturer/src/audio_source.cpp index 92be601e06657b808bbc426bae6f91b432effad3..d17860fe8aab8ce26e84ba2d6a3f27c47bdfda18 100755 --- a/frameworks/innerkitsimpl/audiocapturer/src/audio_source.cpp +++ b/frameworks/innerkitsimpl/audiocapturer/src/audio_source.cpp @@ -27,6 +27,7 @@ namespace Audio { } while (0) using namespace OHOS::Media; +using namespace OHOS::AudioStandard; static AudioManager *g_audioManager = nullptr; AudioSource::AudioSource() : initialized_(false), diff --git a/frameworks/innerkitsimpl/audiorecorder/include/audio_recorder_private.h b/frameworks/innerkitsimpl/audiorecorder/include/audio_recorder_private.h new file mode 100644 index 0000000000000000000000000000000000000000..03354f80e78265d344350662e350d66cd449345f --- /dev/null +++ b/frameworks/innerkitsimpl/audiorecorder/include/audio_recorder_private.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2021 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_recorder.h" +#include "audio_stream.h" + +namespace OHOS { +namespace AudioStandard { +class AudioRecorderPrivate : public AudioRecorder { +public: + int32_t GetFrameCount(uint32_t &frameCount) const override; + int32_t SetParams(const AudioRecorderParams params) const override; + int32_t GetParams(AudioRecorderParams ¶ms) const override; + bool Start() const override; + int32_t Read(uint8_t &buffer, size_t userSize, bool isBlockingRead) const override; + RecorderState GetStatus() const override; + bool GetAudioTime(Timestamp ×tamp, Timestamp::Timestampbase base) const override; + bool Stop() const override; + bool Flush() const override; + bool Release() const override; + int32_t GetBufferSize(size_t &bufferSize) const override; + + std::unique_ptr audioRecorder; + + explicit AudioRecorderPrivate(AudioStreamType audioStreamType); + virtual ~AudioRecorderPrivate(); +}; +} // namespace AudioStandard +} // namespace OHOS diff --git a/frameworks/innerkitsimpl/audiorecorder/src/audio_recorder.cpp b/frameworks/innerkitsimpl/audiorecorder/src/audio_recorder.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f7346036ce4ce5cd825e359c095117282e190d4d --- /dev/null +++ b/frameworks/innerkitsimpl/audiorecorder/src/audio_recorder.cpp @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2021 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 "audio_errors.h" +#include "audio_recorder.h" +#include "audio_recorder_private.h" +#include "audio_stream.h" + +namespace OHOS { +namespace AudioStandard { +AudioRecorder::~AudioRecorder() = default; +AudioRecorderPrivate::~AudioRecorderPrivate() = default; + +std::unique_ptr AudioRecorder::Create(AudioStreamType audioStreamType) +{ + return std::make_unique(audioStreamType); +} + +AudioRecorderPrivate::AudioRecorderPrivate(AudioStreamType audioStreamType) +{ + audioRecorder = std::make_unique(audioStreamType, AUDIO_MODE_RECORD); +} + +int32_t AudioRecorderPrivate::GetFrameCount(uint32_t &frameCount) const +{ + return audioRecorder->GetFrameCount(frameCount); +} + +int32_t AudioRecorderPrivate::SetParams(const AudioRecorderParams params) const +{ + AudioStreamParams audioStreamParams; + audioStreamParams.format = params.audioSampleFormat; + audioStreamParams.samplingRate = params.samplingRate; + audioStreamParams.channels = params.audioChannel; + audioStreamParams.encoding = params.audioEncoding; + + return audioRecorder->SetAudioStreamInfo(audioStreamParams); +} + +int32_t AudioRecorderPrivate::GetParams(AudioRecorderParams ¶ms) const +{ + AudioStreamParams audioStreamParams; + int32_t result = audioRecorder->GetAudioStreamInfo(audioStreamParams); + if (SUCCESS == result) { + params.audioSampleFormat = static_cast(audioStreamParams.format); + params.samplingRate = static_cast(audioStreamParams.samplingRate); + params.audioChannel = static_cast(audioStreamParams.channels); + params.audioEncoding = static_cast(audioStreamParams.encoding); + } + + return result; +} + +bool AudioRecorderPrivate::Start() const +{ + return audioRecorder->StartAudioStream(); +} + +int32_t AudioRecorderPrivate::Read(uint8_t &buffer, size_t userSize, bool isBlockingRead) const +{ + return audioRecorder->Read(buffer, userSize, isBlockingRead); +} + +RecorderState AudioRecorderPrivate::GetStatus() const +{ + return (RecorderState)audioRecorder->GetState(); +} + +bool AudioRecorderPrivate::GetAudioTime(Timestamp ×tamp, Timestamp::Timestampbase base) const +{ + return audioRecorder->GetAudioTime(timestamp, base); +} + +bool AudioRecorderPrivate::Stop() const +{ + return audioRecorder->StopAudioStream(); +} + +bool AudioRecorderPrivate::Flush() const +{ + return audioRecorder->FlushAudioStream(); +} + +bool AudioRecorderPrivate::Release() const +{ + return audioRecorder->ReleaseAudioStream(); +} + +int32_t AudioRecorderPrivate::GetBufferSize(size_t &bufferSize) const +{ + return audioRecorder->GetBufferSize(bufferSize); +} + +std::vector AudioRecorder::GetSupportedFormats() +{ + return AUDIO_SUPPORTED_FORMATS; +} + +std::vector AudioRecorder::GetSupportedChannels() +{ + return AUDIO_SUPPORTED_CHANNELS; +} + +std::vector AudioRecorder::GetSupportedEncodingTypes() +{ + return AUDIO_SUPPORTED_ENCODING_TYPES; +} + +std::vector AudioRecorder::GetSupportedSamplingRates() +{ + return AUDIO_SUPPORTED_SAMPLING_RATES; +} +} // namespace AudioStandard +} // namespace OHOS \ No newline at end of file diff --git a/frameworks/innerkitsimpl/audiorenderer/BUILD.gn b/frameworks/innerkitsimpl/audiorenderer/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..002a9e5c2644fb9ceb751d18e168d7cc15613aa8 --- /dev/null +++ b/frameworks/innerkitsimpl/audiorenderer/BUILD.gn @@ -0,0 +1,61 @@ +# Copyright (C) 2021 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("//drivers/adapter/uhdf2/uhdf.gni") + +ohos_shared_library("audio_renderer_sink") { + install_enable = true + + sources = [ + "//foundation/multimedia/audio_standard/frameworks/innerkitsimpl/audiorenderer/src/audio_renderer_sink.cpp", + ] + + cflags = ["-fPIC"] + cflags += ["-Wall"] + cflags_cc = cflags + + include_dirs = [ + "//foundation/multimedia/audio_standard/frameworks/innerkitsimpl/audiorenderer/include", + "//foundation/multimedia/audio_standard/frameworks/innerkitsimpl/common/include", + "//foundation/multimedia/media_standard/interfaces/innerkits/native/media/include", + "//utils/native/base/include", + "//base/hiviewdfx/hilog/interfaces/native/innerkits/include", + "//drivers/peripheral/audio/interfaces/include", + "//utils/native/base/include", + ] + + public_configs = [":audio_external_library_config"] + + deps = [ + "//third_party/bounds_checking_function:libsec_static", + "//base/hiviewdfx/hilog/interfaces/native/innerkits:libhilog", + "//utils/native/base:utils", + "$hdf_uhdf_path/ipc:libhdf_ipc_adapter", + "$hdf_uhdf_path/hcs:hdf_default.hcb", + "$hdf_uhdf_path/hdi:libhdi", + "$hdf_uhdf_path/host:hdf_devhost", + "$hdf_uhdf_path/host:libhdf_host", + "$hdf_uhdf_path/config:libhdf_hcs", + "$hdf_uhdf_path/manager:hdf_devmgr.rc", + "$hdf_uhdf_path/manager:hdf_devmgr", + "$hdf_uhdf_path/osal:libhdf_utils", + ] + + part_name = "multimedia_audio_standard" + subsystem_name = "multimedia" +} + +config("audio_external_library_config") { + include_dirs = ["//foundation/multimedia/audio_standard/frameworks/innerkitsimpl/common/include"] +} diff --git a/frameworks/innerkitsimpl/audiorenderer/include/audio_renderer_private.h b/frameworks/innerkitsimpl/audiorenderer/include/audio_renderer_private.h new file mode 100644 index 0000000000000000000000000000000000000000..997439e025c509a81812dce1b7ee0207c57cf60e --- /dev/null +++ b/frameworks/innerkitsimpl/audiorenderer/include/audio_renderer_private.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2021 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_RECORDER_PRIVATE_H +#define AUDIO_RECORDER_PRIVATE_H + +#include "audio_renderer.h" +#include "audio_stream.h" + +namespace OHOS { +namespace AudioStandard { +class AudioRendererPrivate : public AudioRenderer { +public: + int32_t GetFrameCount(uint32_t &frameCount) const override; + int32_t GetLatency(uint64_t &latency) const override; + int32_t SetParams(const AudioRendererParams params) const override; + int32_t GetParams(AudioRendererParams ¶ms) const override; + bool Start() const override; + int32_t Write(uint8_t *buffer, size_t bufferSize) override; + RendererState GetStatus() const override; + bool GetAudioTime(Timestamp ×tamp, Timestamp::Timestampbase base) const override; + bool Drain() const override; + bool Pause() const override; + bool Stop() const override; + bool Flush() const override; + bool Release() const override; + int32_t GetBufferSize(size_t &bufferSize) const override; + + std::unique_ptr audioRenderer; + + explicit AudioRendererPrivate(AudioStreamType audioStreamType); + virtual ~AudioRendererPrivate(); +}; +} // namespace AudioStandard +} // namespace OHOS +#endif // AUDIO_RECORDER_PRIVATE_H diff --git a/frameworks/innerkitsimpl/audiorenderer/include/audio_renderer_sink.h b/frameworks/innerkitsimpl/audiorenderer/include/audio_renderer_sink.h new file mode 100644 index 0000000000000000000000000000000000000000..b756adfd5cdbd4a7f8710e41fb7e5572ac9efee0 --- /dev/null +++ b/frameworks/innerkitsimpl/audiorenderer/include/audio_renderer_sink.h @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2021 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_RENDERER_SINK_H +#define AUDIO_RENDERER_SINK_H + +#include "audio_manager.h" + +#include + +namespace OHOS { +namespace AudioStandard { +typedef struct { + AudioFormat format; + uint32_t sampleFmt; + uint32_t sampleRate; + uint32_t channel; + float volume; +} AudioSinkAttr; + +class AudioRendererSink { +public: + int32_t Init(AudioSinkAttr &atrr); + void DeInit(void); + int32_t Start(void); + int32_t Stop(void); + int32_t Flush(void); + int32_t Reset(void); + int32_t Pause(void); + int32_t Resume(void); + int32_t RenderFrame(char &frame, uint64_t len, uint64_t &writeLen); + int32_t SetVolume(float left, float right); + int32_t GetVolume(float &left, float &right); + int32_t GetLatency(uint32_t *latency); + static AudioRendererSink* GetInstance(void); + bool rendererInited_; +private: + AudioRendererSink(); + ~AudioRendererSink(); + AudioSinkAttr attr_; + bool started_; + bool paused_; + float leftVolume_; + float rightVolume_; + struct AudioManager *audioManager_; + struct AudioAdapter *audioAdapter_; + struct AudioRender *audioRender_; + void *handle_; + + int32_t CreateRender(struct AudioPort &renderPort); + int32_t InitAudioManager(); +#ifdef DUMPFILE + FILE *pfd; +#endif // DUMPFILE +}; +} // namespace AudioStandard +} // namespace OHOS +#endif // AUDIO_RENDERER_SINK_H diff --git a/frameworks/innerkitsimpl/audiorenderer/include/audio_renderer_sink_intf.h b/frameworks/innerkitsimpl/audiorenderer/include/audio_renderer_sink_intf.h new file mode 100644 index 0000000000000000000000000000000000000000..6219e31c01d434f185db1352e3ce26f34d30c203 --- /dev/null +++ b/frameworks/innerkitsimpl/audiorenderer/include/audio_renderer_sink_intf.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2020 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_RENDERER_SINK_INTF_H +#define AUDIO_RENDERER_SINK_INTF_H + +#ifdef __cplusplus +extern "C" { +#endif +typedef struct { + enum AudioFormat format; + uint32_t sampleFmt; + uint32_t sampleRate; + uint32_t channel; + float volume; +} AudioSinkAttr; + +int32_t AudioRendererSinkInit(AudioSinkAttr *); +void AudioRendererSinkDeInit(void); +int32_t AudioRendererSinkStart(void); +int32_t AudioRendererSinkStop(void); +int32_t AudioRendererRenderFrame(char*, uint64_t, uint64_t*); +int32_t AudioRendererSinkSetVolume(float, float); +int32_t AudioRendererSinkGetLatency(uint32_t *); +#ifdef __cplusplus +} +#endif +#endif // AUDIO_RENDERER_SINK_INTF_H diff --git a/frameworks/innerkitsimpl/audiorenderer/src/audio_renderer.cpp b/frameworks/innerkitsimpl/audiorenderer/src/audio_renderer.cpp new file mode 100644 index 0000000000000000000000000000000000000000..df37e8ca5352bc0455014b3b94c9787ef9786bc5 --- /dev/null +++ b/frameworks/innerkitsimpl/audiorenderer/src/audio_renderer.cpp @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2021 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 "audio_errors.h" +#include "audio_renderer.h" +#include "audio_renderer_private.h" +#include "audio_stream.h" + +namespace OHOS { +namespace AudioStandard { +AudioRenderer::~AudioRenderer() = default; +AudioRendererPrivate::~AudioRendererPrivate() = default; + +std::unique_ptr AudioRenderer::Create(AudioStreamType audioStreamType) +{ + return std::make_unique(audioStreamType); +} + +AudioRendererPrivate::AudioRendererPrivate(AudioStreamType audioStreamType) +{ + audioRenderer = std::make_unique(audioStreamType, AUDIO_MODE_PLAYBACK); +} + +int32_t AudioRendererPrivate::GetFrameCount(uint32_t &frameCount) const +{ + return audioRenderer->GetFrameCount(frameCount); +} + +int32_t AudioRendererPrivate::GetLatency(uint64_t &latency) const +{ + return audioRenderer->GetLatency(latency); +} + +int32_t AudioRendererPrivate::SetParams(const AudioRendererParams params) const +{ + AudioStreamParams audioStreamParams; + audioStreamParams.format = params.sampleFormat; + audioStreamParams.samplingRate = params.sampleRate; + audioStreamParams.channels = params.channelCount; + audioStreamParams.encoding = params.encodingType; + + return audioRenderer->SetAudioStreamInfo(audioStreamParams); +} + +int32_t AudioRendererPrivate::GetParams(AudioRendererParams ¶ms) const +{ + AudioStreamParams audioStreamParams; + int32_t result = audioRenderer->GetAudioStreamInfo(audioStreamParams); + if (!result) { + params.sampleFormat = static_cast(audioStreamParams.format); + params.sampleRate = static_cast(audioStreamParams.samplingRate); + params.channelCount = static_cast(audioStreamParams.channels); + params.encodingType = static_cast(audioStreamParams.encoding); + } + + return result; +} + +bool AudioRendererPrivate::Start() const +{ + return audioRenderer->StartAudioStream(); +} + +int32_t AudioRendererPrivate::Write(uint8_t *buffer, size_t bufferSize) +{ + return audioRenderer->Write(buffer, bufferSize); +} + +RendererState AudioRendererPrivate::GetStatus() const +{ + return static_cast(audioRenderer->GetState()); +} + +bool AudioRendererPrivate::GetAudioTime(Timestamp ×tamp, Timestamp::Timestampbase base) const +{ + return audioRenderer->GetAudioTime(timestamp, base); +} + +bool AudioRendererPrivate::Drain() const +{ + return audioRenderer->DrainAudioStream(); +} + +bool AudioRendererPrivate::Flush() const +{ + return audioRenderer->FlushAudioStream(); +} + +bool AudioRendererPrivate::Pause() const +{ + return audioRenderer->PauseAudioStream(); +} + +bool AudioRendererPrivate::Stop() const +{ + return audioRenderer->StopAudioStream(); +} + +bool AudioRendererPrivate::Release() const +{ + return audioRenderer->ReleaseAudioStream(); +} + +int32_t AudioRendererPrivate::GetBufferSize(size_t &bufferSize) const +{ + return audioRenderer->GetBufferSize(bufferSize); +} + +std::vector AudioRenderer::GetSupportedFormats() +{ + return AUDIO_SUPPORTED_FORMATS; +} + +std::vector AudioRenderer::GetSupportedSamplingRates() +{ + return AUDIO_SUPPORTED_SAMPLING_RATES; +} + +std::vector AudioRenderer::GetSupportedChannels() +{ + return AUDIO_SUPPORTED_CHANNELS; +} + +std::vector AudioRenderer::GetSupportedEncodingTypes() +{ + return AUDIO_SUPPORTED_ENCODING_TYPES; +} +} // namespace AudioStandard +} // namespace OHOS diff --git a/frameworks/innerkitsimpl/audiorenderer/src/audio_renderer_sink.cpp b/frameworks/innerkitsimpl/audiorenderer/src/audio_renderer_sink.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c0271f061fb749d7ff73cc4d5453e45707ef468c --- /dev/null +++ b/frameworks/innerkitsimpl/audiorenderer/src/audio_renderer_sink.cpp @@ -0,0 +1,454 @@ +/* + * Copyright (C) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +#include "audio_errors.h" +#include "audio_renderer_sink.h" +#include "media_log.h" + +namespace OHOS { +namespace AudioStandard { +namespace { +const int32_t HALF_FACTOR = 2; +const int32_t MAX_AUDIO_ADAPTER_NUM = 3; +const float DEFAULT_VOLUME_LEVEL = 1.0f; +const uint32_t AUDIO_CHANNELCOUNT = 2; +const uint32_t AUDIO_SAMPLE_RATE_48K = 48000; +const uint32_t DEEP_BUFFER_RENDER_PERIOD_SIZE = 4096; +const uint32_t INT_32_MAX = 0x7fffffff; +#define PCM_8_BIT 8 +#define PCM_16_BIT 16 +} + +#ifdef DUMPFILE +const char *g_audioOutTestFilePath = "/data/local/tmp/audioout_test.pcm"; +#endif // DUMPFILE + +AudioRendererSink::AudioRendererSink() + : rendererInited_(false), started_(false), paused_(false), leftVolume_(DEFAULT_VOLUME_LEVEL), + rightVolume_(DEFAULT_VOLUME_LEVEL), audioManager_(nullptr), audioAdapter_(nullptr), audioRender_(nullptr) +{ + attr_ = {}; +#ifdef DUMPFILE + pfd = nullptr; +#endif // DUMPFILE +} + +AudioRendererSink::~AudioRendererSink() +{ + DeInit(); +} + +AudioRendererSink* AudioRendererSink::GetInstance() +{ + static AudioRendererSink audioRenderer_; + + return &audioRenderer_; +} + +void AudioRendererSink::DeInit() +{ + started_ = false; + rendererInited_ = false; + if ((audioRender_ != nullptr) && (audioAdapter_ != nullptr)) { + audioAdapter_->DestroyRender(audioAdapter_, audioRender_); + } + audioRender_ = nullptr; + + if ((audioManager_ != nullptr) && (audioAdapter_ != nullptr)) { + audioManager_->UnloadAdapter(audioManager_, audioAdapter_); + } + audioAdapter_ = nullptr; + audioManager_ = nullptr; + + dlclose(handle_); + +#ifdef DUMPFILE + if (pfd) { + fclose(pfd); + pfd = nullptr; + } +#endif // DUMPFILE +} + +int32_t InitAttrs(struct AudioSampleAttributes &attrs) +{ + /* Initialization of audio parameters for playback */ + attrs.format = AUDIO_FORMAT_PCM_16_BIT; + attrs.channelCount = AUDIO_CHANNELCOUNT; + attrs.sampleRate = AUDIO_SAMPLE_RATE_48K; + attrs.interleaved = 0; + attrs.type = AUDIO_IN_MEDIA; + attrs.period = DEEP_BUFFER_RENDER_PERIOD_SIZE; + attrs.frameSize = PCM_16_BIT * attrs.channelCount / PCM_8_BIT; + attrs.isBigEndian = false; + attrs.isSignedData = true; + attrs.startThreshold = DEEP_BUFFER_RENDER_PERIOD_SIZE / (attrs.frameSize); + attrs.stopThreshold = INT_32_MAX; + attrs.silenceThreshold = 0; + + return SUCCESS; +} + +static int32_t SwitchAdapter(struct AudioAdapterDescriptor *descs, const char *adapterNameCase, + enum AudioPortDirection portFlag, struct AudioPort *renderPort, int32_t size) +{ + for (int32_t index = 0; index < size; index++) { + struct AudioAdapterDescriptor *desc = &descs[index]; + if (desc == nullptr) { + MEDIA_ERR_LOG("SwitchAdapter Fail AudioAdapterDescriptor desc NULL"); + return ERR_INVALID_INDEX; // note: return invalid index not invalid handle + } + if (!strcmp(desc->adapterName, adapterNameCase)) { + for (uint32_t port = 0; ((desc != NULL) && (port < desc->portNum)); port++) { + // Only find out the port of out in the sound card + if (desc->ports[port].dir == portFlag) { + *renderPort = desc->ports[port]; + return index; + } + } + } + } + MEDIA_ERR_LOG("SwitchAdapter Fail"); + + return ERR_INVALID_INDEX; +} + +int32_t AudioRendererSink::InitAudioManager() +{ + char resolvedPath[100] = "/system/lib/libhdi_audio.z.so"; + struct AudioManager *(*getAudioManager)() = nullptr; + + handle_ = dlopen(resolvedPath, 1); + if (handle_ == nullptr) { + MEDIA_ERR_LOG("Open so Fail"); + return ERR_INVALID_HANDLE; + } + + getAudioManager = (struct AudioManager* (*)())(dlsym(handle_, "GetAudioManagerFuncs")); + audioManager_ = getAudioManager(); + if (audioManager_ == nullptr) { + return ERR_INVALID_HANDLE; + } + + return 0; +} + +int32_t AudioRendererSink::CreateRender(struct AudioPort &renderPort) +{ + // Initialization port information, can fill through mode and other parameters + int32_t ret = audioAdapter_->InitAllPorts(audioAdapter_); + if (ret != 0) { + MEDIA_ERR_LOG("InitAllPorts failed"); + return ERR_NOT_STARTED; + } + + struct AudioSampleAttributes param; + InitAttrs(param); + param.sampleRate = attr_.sampleRate; + param.channelCount = attr_.channel; + + struct AudioDeviceDescriptor deviceDesc; + deviceDesc.portId = renderPort.portId; + deviceDesc.pins = PIN_OUT_SPEAKER; + deviceDesc.desc = nullptr; + ret = audioAdapter_->CreateRender(audioAdapter_, &deviceDesc, ¶m, &audioRender_); + if (ret != 0 || audioRender_ == nullptr) { + MEDIA_ERR_LOG("AudioDeviceCreateRender failed"); + audioManager_->UnloadAdapter(audioManager_, audioAdapter_); + return ERR_NOT_STARTED; + } + + rendererInited_ = true; + return 0; +} + +int32_t AudioRendererSink::Init(AudioSinkAttr &attr) +{ + attr_ = attr; + struct AudioPort renderPort; + const char *adapterNameCase = "usb"; // Set sound card information + enum AudioPortDirection port = PORT_OUT; // Set port information + + if (InitAudioManager() != 0) { + MEDIA_ERR_LOG("Init audio manager Fail"); + return ERR_NOT_STARTED; + } + + int32_t size = -1; + struct AudioAdapterDescriptor *descs = nullptr; + audioManager_->GetAllAdapters(audioManager_, &descs, &size); + if (size > MAX_AUDIO_ADAPTER_NUM || size == 0 || descs == nullptr) { + MEDIA_ERR_LOG("Get adapters Fail"); + return ERR_NOT_STARTED; + } + + // Get qualified sound card and port + int32_t index = SwitchAdapter(descs, adapterNameCase, port, &renderPort, size); + if (index < 0) { + MEDIA_ERR_LOG("Switch Adapter Fail"); + return ERR_NOT_STARTED; + } + + struct AudioAdapterDescriptor *desc = &descs[index]; + if (audioManager_->LoadAdapter(audioManager_, desc, &audioAdapter_) != 0) { + MEDIA_ERR_LOG("Load Adapter Fail"); + return ERR_NOT_STARTED; + } + if (audioAdapter_ == nullptr) { + MEDIA_ERR_LOG("Load audio device failed"); + return ERR_NOT_STARTED; + } + + if (CreateRender(renderPort) != 0) { + MEDIA_ERR_LOG("Create render failed"); + return ERR_NOT_STARTED; + } + +#ifdef DUMPFILE + pfd = fopen(g_audioOutTestFilePath, "wb+"); + if (pfd == nullptr) { + MEDIA_ERR_LOG("Error opening pcm test file!"); + } +#endif // DUMPFILE + + return SUCCESS; +} + +int32_t AudioRendererSink::RenderFrame(char &data, uint64_t len, uint64_t &writeLen) +{ + int32_t ret; + if (audioRender_ == nullptr) { + MEDIA_ERR_LOG("Audio Render Handle is nullptr!"); + return ERR_INVALID_HANDLE; + } + +#ifdef DUMPFILE + size_t writeResult = fwrite((void*)&data, 1, len, pfd); + if (writeResult != len) { + MEDIA_ERR_LOG("Failed to write the file."); + } +#endif // DUMPFILE + + ret = audioRender_->RenderFrame(audioRender_, (void*)&data, len, &writeLen); + if (ret != 0) { + MEDIA_ERR_LOG("RenderFrame failed ret: %{public}x", ret); + return ERR_WRITE_FAILED; + } + + return SUCCESS; +} + +int32_t AudioRendererSink::Start(void) +{ + if (!started_ && audioRender_ != nullptr) { + audioRender_->control.Start((AudioHandle)audioRender_); + started_ = true; + } + return SUCCESS; +} + +int32_t AudioRendererSink::SetVolume(float left, float right) +{ + float volume; + if (audioRender_ == nullptr) { + MEDIA_ERR_LOG("AudioRendererSink::SetVolume failed audioRender_ null"); + return ERR_INVALID_HANDLE; + } + + leftVolume_ = left; + rightVolume_ = right; + if ((leftVolume_ == 0) && (rightVolume_ != 0)) { + volume = rightVolume_; + } else if ((leftVolume_ != 0) && (rightVolume_ == 0)) { + volume = leftVolume_; + } else { + volume = (leftVolume_ + rightVolume_) / HALF_FACTOR; + } + audioRender_->volume.SetVolume(reinterpret_cast(audioRender_), volume); + return SUCCESS; +} + +int32_t AudioRendererSink::GetVolume(float &left, float &right) +{ + left = leftVolume_; + right = rightVolume_; + return SUCCESS; +} + +int32_t AudioRendererSink::GetLatency(uint32_t *latency) +{ + if (audioRender_ == nullptr) { + MEDIA_ERR_LOG("AudioRendererSink: GetLatency failed audio render null"); + return ERR_INVALID_HANDLE; + } + + if (!latency) { + MEDIA_ERR_LOG("AudioRendererSink: GetLatency failed latency null"); + return ERR_INVALID_PARAM; + } + + uint32_t hdiLatency; + if (audioRender_->GetLatency(audioRender_, &hdiLatency) == 0) { + *latency = hdiLatency; + MEDIA_INFO_LOG("AudioRendererSink: Latency: %{public}u", *latency); + return SUCCESS; + } else { + return ERR_OPERATION_FAILED; + } +} + +int32_t AudioRendererSink::Stop(void) +{ + if (started_ && audioRender_ != nullptr) { + audioRender_->control.Stop(reinterpret_cast(audioRender_)); + } + started_ = false; + return SUCCESS; +} + +int32_t AudioRendererSink::Pause(void) +{ + if (started_ && audioRender_ != nullptr) { + audioRender_->control.Pause(reinterpret_cast(audioRender_)); + } + paused_ = true; + return SUCCESS; +} + +int32_t AudioRendererSink::Resume(void) +{ + audioRender_->control.Resume(reinterpret_cast(audioRender_)); + paused_ = false; + return SUCCESS; +} + +int32_t AudioRendererSink::Reset(void) +{ + if (started_ && audioRender_ != nullptr) { + audioRender_->control.Flush(reinterpret_cast(audioRender_)); + } + return SUCCESS; +} + +int32_t AudioRendererSink::Flush(void) +{ + if (started_ && audioRender_ != nullptr) { + audioRender_->control.Flush(reinterpret_cast(audioRender_)); + } + return SUCCESS; +} +} // namespace AudioStandard +} // namespace OHOS + +#ifdef __cplusplus +extern "C" { +#endif + +using namespace OHOS::AudioStandard; + +AudioRendererSink* g_audioRendrSinkInstance = AudioRendererSink::GetInstance(); + +int32_t AudioRendererSinkInit(AudioSinkAttr *attr) +{ + int32_t ret; + + if (g_audioRendrSinkInstance->rendererInited_) + return SUCCESS; + + ret = g_audioRendrSinkInstance->Init(*attr); + return ret; +} + +void AudioRendererSinkDeInit() +{ + if (g_audioRendrSinkInstance->rendererInited_) + g_audioRendrSinkInstance->DeInit(); +} + +int32_t AudioRendererSinkStop() +{ + int32_t ret; + + if (!g_audioRendrSinkInstance->rendererInited_) + return SUCCESS; + + ret = g_audioRendrSinkInstance->Stop(); + return ret; +} + +int32_t AudioRendererSinkStart() +{ + int32_t ret; + + if (!g_audioRendrSinkInstance->rendererInited_) { + MEDIA_ERR_LOG("audioRenderer Not Inited! Init the renderer first\n"); + return ERR_NOT_STARTED; + } + + ret = g_audioRendrSinkInstance->Start(); + return ret; +} + +int32_t AudioRendererRenderFrame(char &data, uint64_t len, uint64_t &writeLen) +{ + int32_t ret; + + if (!g_audioRendrSinkInstance->rendererInited_) { + MEDIA_ERR_LOG("audioRenderer Not Inited! Init the renderer first\n"); + return ERR_NOT_STARTED; + } + + ret = g_audioRendrSinkInstance->RenderFrame(data, len, writeLen); + return ret; +} + +int32_t AudioRendererSinkSetVolume(float left, float right) +{ + int32_t ret; + + if (!g_audioRendrSinkInstance->rendererInited_) { + MEDIA_ERR_LOG("audioRenderer Not Inited! Init the renderer first\n"); + return ERR_NOT_STARTED; + } + + ret = g_audioRendrSinkInstance->SetVolume(left, right); + return ret; +} + +int32_t AudioRendererSinkGetLatency(uint32_t *latency) +{ + int32_t ret; + + if (!g_audioRendrSinkInstance->rendererInited_) { + MEDIA_ERR_LOG("audioRenderer Not Inited! Init the renderer first\n"); + return ERR_NOT_STARTED; + } + + if (!latency) { + MEDIA_ERR_LOG("AudioRendererSinkGetLatency failed latency null"); + return ERR_INVALID_PARAM; + } + + ret = g_audioRendrSinkInstance->GetLatency(latency); + return ret; +} + +#ifdef __cplusplus +} +#endif diff --git a/frameworks/innerkitsimpl/common/include/media_errors.h b/frameworks/innerkitsimpl/common/include/audio_errors.h old mode 100755 new mode 100644 similarity index 39% rename from frameworks/innerkitsimpl/common/include/media_errors.h rename to frameworks/innerkitsimpl/common/include/audio_errors.h index 3e4da13ed8a1743296021e1d86ee4a6d3ebd38b5..bd076132c6fb24830d770fd1496b91d8d10630f0 --- a/frameworks/innerkitsimpl/common/include/media_errors.h +++ b/frameworks/innerkitsimpl/common/include/audio_errors.h @@ -1,104 +1,119 @@ -/* - * Copyright (C) 2021 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. - */ - -/** - * @addtogroup MultiMedia_MediaCommon - * @{ - * - * @brief Provides data types and media formats required for recording and playing audio and videos. - * - * - * @since 1.0 - * @version 1.0 - */ - -/** - * @file media_errors.h - * - * @brief Declares the media_errors class to define errors that may occur during media operations. - * - * - * @since 1.0 - * @version 1.0 - */ - -#ifndef MEDIA_ERRORS_H -#define MEDIA_ERRORS_H - -#include - -namespace OHOS { -namespace Media { -constexpr int MODULE_MEDIA = 1; -constexpr int SUBSYS_MEDIA = 30; - -using ErrCode = int32_t; -constexpr int SUBSYSTEM_BIT_NUM = 21; -constexpr int MODULE_BIT_NUM = 16; - -/** - * @brief Generates a start error code with a unique identifier based on specified subsystem and module bit numbers. - * - * @param subsystem Indicates the subsystem bit number. - * @param module Indicates the module bit number. - * @return - * @since 1.0 - * @version 1.0 - */ -constexpr ErrCode ErrCodeOffset(unsigned int subsystem, unsigned int module = 0) -{ - return (subsystem << SUBSYSTEM_BIT_NUM) | (module << MODULE_BIT_NUM); -} - -constexpr int32_t BASE_MEDIA_ERR_OFFSET = ErrCodeOffset(SUBSYS_MEDIA, MODULE_MEDIA); - -/** Invalid data size that has been read */ -const int32_t ERR_INVALID_READ = -1; - -/** Success */ -const int32_t SUCCESS = 0; - -/** Fail */ -const int32_t ERROR = BASE_MEDIA_ERR_OFFSET; - -/** Status error */ -const int32_t ERR_ILLEGAL_STATE = BASE_MEDIA_ERR_OFFSET + 1; - -/** Invalid parameter */ -const int32_t ERR_INVALID_PARAM = BASE_MEDIA_ERR_OFFSET + 2; - -/** Early media preparation */ -const int32_t ERR_EARLY_PREPARE = BASE_MEDIA_ERR_OFFSET + 3; - -/** No media source */ -const int32_t ERR_SOURCE_NOT_SET = BASE_MEDIA_ERR_OFFSET + 4; - -/** Invalid operation */ -const int32_t ERR_INVALID_OPERATION = BASE_MEDIA_ERR_OFFSET + 5; - -/** No idle channel */ -const int32_t ERR_NOFREE_CHANNEL = BASE_MEDIA_ERR_OFFSET + 6; - -/** Buffer reading failed */ -const int32_t ERR_READ_BUFFER = BASE_MEDIA_ERR_OFFSET + 7; - -/** Device not started */ -const int32_t ERR_NOT_STARTED = BASE_MEDIA_ERR_OFFSET + 8; - -/** Unknown error */ -const int32_t ERR_UNKNOWN = BASE_MEDIA_ERR_OFFSET + 200; -} // namespace Media -} // namespace OHOS -#endif // MEDIA_ERRORS_H +/* + * Copyright (C) 2021 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. + */ + +/** + * @addtogroup MultiMedia_AudioCommon + * @{ + * + * @brief Provides data types and audio formats required for recording and playing and recording audio. + * + * + * @since 1.0 + * @version 1.0 + */ + +/** + * @file audio_errors.h + * + * @brief Declares the audio_errors class to define errors that may occur during audio operations. + * + * + * @since 1.0 + * @version 1.0 + */ + +#ifndef AUDIO_ERRORS_H +#define AUDIO_ERRORS_H + +#include + +namespace OHOS { +namespace AudioStandard { +constexpr int MODULE_AUDIO = 1; +constexpr int SUBSYS_AUDIO = 30; + +using ErrCode = int32_t; +constexpr int SUBSYSTEM_BIT_NUM = 21; +constexpr int MODULE_BIT_NUM = 16; + +constexpr ErrCode ErrCodeOffset(unsigned int subsystem, unsigned int module = 0) +{ + return (subsystem << SUBSYSTEM_BIT_NUM) | (module << MODULE_BIT_NUM); +} + +constexpr int32_t BASE_AUDIO_ERR_OFFSET = -ErrCodeOffset(SUBSYS_AUDIO, MODULE_AUDIO); + +/** Success */ +const int32_t SUCCESS = 0; + +/** Fail */ +const int32_t ERROR = BASE_AUDIO_ERR_OFFSET; + +/** Status error */ +const int32_t ERR_ILLEGAL_STATE = BASE_AUDIO_ERR_OFFSET - 1; + +/** Invalid parameter */ +const int32_t ERR_INVALID_PARAM = BASE_AUDIO_ERR_OFFSET - 2; + +/** Early media preparation */ +const int32_t ERR_EARLY_PREPARE = BASE_AUDIO_ERR_OFFSET - 3; + +/** Invalid operation */ +const int32_t ERR_INVALID_OPERATION = BASE_AUDIO_ERR_OFFSET - 4; + +/** error operation failed */ +const int32_t ERR_OPERATION_FAILED = BASE_AUDIO_ERR_OFFSET - 5; + +/** Buffer reading failed */ +const int32_t ERR_READ_BUFFER = BASE_AUDIO_ERR_OFFSET - 6; + +/** Buffer writing failed */ +const int32_t ERR_WRITE_BUFFER = BASE_AUDIO_ERR_OFFSET - 7; + +/** Device not started */ +const int32_t ERR_NOT_STARTED = BASE_AUDIO_ERR_OFFSET - 8; + +/** Invalid Device handle */ +const int32_t ERR_INVALID_HANDLE = BASE_AUDIO_ERR_OFFSET - 9; + +/** unsupported operation */ +const int32_t ERR_NOT_SUPPORTED = BASE_AUDIO_ERR_OFFSET - 10; + +/** unsupported device */ +const int32_t ERR_DEVICE_NOT_SUPPORTED = BASE_AUDIO_ERR_OFFSET - 11; + +/** write operation failed */ +const int32_t ERR_WRITE_FAILED = BASE_AUDIO_ERR_OFFSET - 12; + +/** read operation failed */ +const int32_t ERR_READ_FAILED = BASE_AUDIO_ERR_OFFSET - 13; + +/** device init failed */ +const int32_t ERR_DEVICE_INIT = BASE_AUDIO_ERR_OFFSET - 14; + +/** Invalid data size that has been read */ +const int32_t ERR_INVALID_READ = BASE_AUDIO_ERR_OFFSET - 15; + +/** Invalid data size that has been written */ +const int32_t ERR_INVALID_WRITE = BASE_AUDIO_ERR_OFFSET - 16; + +/** set invalid index < 0 */ +const int32_t ERR_INVALID_INDEX = BASE_AUDIO_ERR_OFFSET - 17; + +/** Unknown error */ +const int32_t ERR_UNKNOWN = BASE_AUDIO_ERR_OFFSET - 200; +} // namespace AudioStandard +} // namespace OHOS +#endif // AUDIO_ERRORS_H diff --git a/frameworks/innerkitsimpl/libsnd/BUILD.gn b/frameworks/innerkitsimpl/libsnd/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..df184f0ab112d93087a947cc4062a9bf37c4c38b --- /dev/null +++ b/frameworks/innerkitsimpl/libsnd/BUILD.gn @@ -0,0 +1,132 @@ +import("//build/ohos.gni") + +libsndfile_dir = "//third_party/libsnd" +libsndfile_build_path = "//foundation/multimedia/audio_standard/frameworks/innerkitsimpl/libsnd" + +config("sndfile_config") { + visibility = [ ":*" ] + + include_dirs = [ + "$libsndfile_dir/src", + "$libsndfile_dir/include", + "$libsndfile_build_path/include", + "$libsndfile_dir/src/GSM610", + "$libsndfile_dir/src/G72x", + "$libsndfile_dir/src/ALAC", + ] + + cflags = [ + "-Wall", + "-Werror", + ] +} + +ohos_source_set("sndfile_sources") { + sources = [ + "$libsndfile_dir/src/common.c", + "$libsndfile_dir/src/file_io.c", + "$libsndfile_dir/src/command.c", + "$libsndfile_dir/src/pcm.c", + "$libsndfile_dir/src/ulaw.c", + "$libsndfile_dir/src/alaw.c", + "$libsndfile_dir/src/float32.c", + "$libsndfile_dir/src/double64.c", + "$libsndfile_dir/src/ima_adpcm.c", + "$libsndfile_dir/src/ms_adpcm.c", + "$libsndfile_dir/src/gsm610.c", + "$libsndfile_dir/src/dwvw.c", + "$libsndfile_dir/src/vox_adpcm.c", + "$libsndfile_dir/src/interleave.c", + "$libsndfile_dir/src/strings.c", + "$libsndfile_dir/src/dither.c", + "$libsndfile_dir/src/cart.c", + "$libsndfile_dir/src/broadcast.c", + "$libsndfile_dir/src/audio_detect.c", + "$libsndfile_dir/src/ima_oki_adpcm.c", + "$libsndfile_dir/src/alac.c", + "$libsndfile_dir/src/chunk.c", + "$libsndfile_dir/src/ogg.c", + "$libsndfile_dir/src/chanmap.c", + "$libsndfile_dir/src/id3.c", + "$libsndfile_dir/src/sndfile.c", + "$libsndfile_dir/src/aiff.c", + "$libsndfile_dir/src/au.c", + "$libsndfile_dir/src/avr.c", + "$libsndfile_dir/src/caf.c", + "$libsndfile_dir/src/dwd.c", + "$libsndfile_dir/src/flac.c", + "$libsndfile_dir/src/g72x.c", + "$libsndfile_dir/src/htk.c", + "$libsndfile_dir/src/ircam.c", + "$libsndfile_dir/src/macos.c", + "$libsndfile_dir/src/mat4.c", + "$libsndfile_dir/src/mat5.c", + "$libsndfile_dir/src/nist.c", + "$libsndfile_dir/src/paf.c", + "$libsndfile_dir/src/pvf.c", + "$libsndfile_dir/src/raw.c", + "$libsndfile_dir/src/rx2.c", + "$libsndfile_dir/src/sd2.c", + "$libsndfile_dir/src/sds.c", + "$libsndfile_dir/src/svx.c", + "$libsndfile_dir/src/txw.c", + "$libsndfile_dir/src/voc.c", + "$libsndfile_dir/src/wve.c", + "$libsndfile_dir/src/w64.c", + "$libsndfile_dir/src/wavlike.c", + "$libsndfile_dir/src/wav.c", + "$libsndfile_dir/src/xi.c", + "$libsndfile_dir/src/mpc2k.c", + "$libsndfile_dir/src/rf64.c", + "$libsndfile_dir/src/ogg_vorbis.c", + "$libsndfile_dir/src/ogg_speex.c", + "$libsndfile_dir/src/ogg_pcm.c", + "$libsndfile_dir/src/ogg_opus.c", + "$libsndfile_dir/src/ogg_vcomment.c", + "$libsndfile_dir/src/nms_adpcm.c", + #"$libsndfile_dir/src/mpeg.c", + #"$libsndfile_dir/src/mpeg_decode.c", + #"$libsndfile_dir/src/mpeg_l3_encode.c", + "$libsndfile_dir/src/GSM610/add.c", + "$libsndfile_dir/src/GSM610/code.c", + "$libsndfile_dir/src/GSM610/decode.c", + "$libsndfile_dir/src/GSM610/gsm_create.c", + "$libsndfile_dir/src/GSM610/gsm_decode.c", + "$libsndfile_dir/src/GSM610/gsm_destroy.c", + "$libsndfile_dir/src/GSM610/gsm_encode.c", + "$libsndfile_dir/src/GSM610/gsm_option.c", + "$libsndfile_dir/src/GSM610/long_term.c", + "$libsndfile_dir/src/GSM610/lpc.c", + "$libsndfile_dir/src/GSM610/preprocess.c", + "$libsndfile_dir/src/GSM610/rpe.c", + "$libsndfile_dir/src/GSM610/short_term.c", + "$libsndfile_dir/src/GSM610/table.c", + "$libsndfile_dir/src/G72x/g721.c", + "$libsndfile_dir/src/G72x/g723_16.c", + "$libsndfile_dir/src/G72x/g723_24.c", + "$libsndfile_dir/src/G72x/g723_40.c", + "$libsndfile_dir/src/G72x/g72x.c", + "$libsndfile_dir/src/ALAC/ALACBitUtilities.c", + "$libsndfile_dir/src/ALAC/ag_dec.c", + "$libsndfile_dir/src/ALAC/ag_enc.c", + "$libsndfile_dir/src/ALAC/dp_dec.c", + "$libsndfile_dir/src/ALAC/dp_enc.c", + "$libsndfile_dir/src/ALAC/matrix_dec.c", + "$libsndfile_dir/src/ALAC/matrix_enc.c", + "$libsndfile_dir/src/ALAC/alac_decoder.c", + "$libsndfile_dir/src/ALAC/alac_encoder.c", + ] + + configs = [ + ":sndfile_config", + ] +} + +ohos_shared_library("sndfile") { + deps = [ + ":sndfile_sources", + ] + + subsystem_name = "multimedia" + part_name = "multimedia_audio_standard" +} diff --git a/frameworks/innerkitsimpl/libsnd/include/config.h b/frameworks/innerkitsimpl/libsnd/include/config.h new file mode 100644 index 0000000000000000000000000000000000000000..a3856c9658b179b4818268087286c3ab55707c60 --- /dev/null +++ b/frameworks/innerkitsimpl/libsnd/include/config.h @@ -0,0 +1,320 @@ +/* Set to 1 if the compile is GNU GCC. */ +#define COMPILER_IS_GCC 1 + +/* Target processor clips on negative float to int conversion. */ +#define CPU_CLIPS_NEGATIVE 0 + +/* Target processor clips on positive float to int conversion. */ +#define CPU_CLIPS_POSITIVE 0 + +/* Target processor is big endian. */ +#define CPU_IS_BIG_ENDIAN 0 + +/* Target processor is little endian. */ +#define CPU_IS_LITTLE_ENDIAN 1 + +/* Set to 1 to enable experimental code. */ +#define ENABLE_EXPERIMENTAL_CODE 0 + +/* Define to 1 if you have the header file. */ +#define HAVE_ALSA_ASOUNDLIB_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_BYTESWAP_H 1 + +/* Define to 1 if you have the `calloc' function. */ +#define HAVE_CALLOC 0 + +/* Define to 1 if you have the `ceil' function. */ +#define HAVE_CEIL 0 + +/* Set to 1 if S_IRGRP is defined. */ +#define HAVE_DECL_S_IRGRP 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_DLFCN_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_DIRECT_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_ENDIAN_H 1 + +/* Will be set to 1 if flac, ogg and vorbis are available. */ +#define HAVE_EXTERNAL_XIPH_LIBS 0 + +/* Will be set to 1 if lame and mpg123 are available. */ +#define HAVE_MPEG 0 + +/* Define to 1 if you have the `floor' function. */ +#define HAVE_FLOOR 0 + +/* Define to 1 if you have the `fmod' function. */ +#define HAVE_FMOD 0 + +/* Define to 1 if you have the `free' function. */ +#define HAVE_FREE 0 + +/* Define to 1 if you have the `fstat' function. */ +#define HAVE_FSTAT 1 + +/* Define to 1 if you have the `fstat64' function. */ +#define HAVE_FSTAT64 1 + +/* Define to 1 if you have the `fsync' function. */ +#define HAVE_FSYNC 1 + +/* Define to 1 if you have the `ftruncate' function. */ +#define HAVE_FTRUNCATE 1 + +/* Define to 1 if you have the `getpagesize' function. */ +#define HAVE_GETPAGESIZE 0 + +/* Define to 1 if you have the `gettimeofday' function. */ +#define HAVE_GETTIMEOFDAY 1 + +/* Define if you have the `gmtime' function. */ +#define HAVE_GMTIME + +/* Define if you have the `gmtime_r' function. */ +#define HAVE_GMTIME_R + +/* Define to 1 if you have the header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_IO_H */ + +/* Define to 1 if you have the `m' library (-lm). */ +#define HAVE_LIBM 0 + +/* Define to 1 if you have the header file. */ +#define HAVE_LOCALE_H 1 + +/* Define if you have the `localtime' function. */ +#define HAVE_LOCALTIME + +/* Define if you have the `localtime_r' function. */ +#define HAVE_LOCALTIME_R + +/* Define if you have C99's lrint function. */ +#define HAVE_LRINT 1 + +/* Define if you have C99's lrintf function. */ +#define HAVE_LRINTF 1 + +/* Define to 1 if you have the `lround' function. */ +#define HAVE_LROUND 0 + +/* Define to 1 if you have the `lseek' function. */ +#define HAVE_LSEEK 1 + +/* Define to 1 if you have the `lseek64' function. */ +#define HAVE_LSEEK64 0 + +/* Define to 1 if you have the `malloc' function. */ +#define HAVE_MALLOC 0 + +/* Define to 1 if you have the header file. */ +#define HAVE_MEMORY_H 0 + +/* Define to 1 if you have the `mmap' function. */ +#define HAVE_MMAP 0 + +/* Define to 1 if you have the `open' function. */ +#define HAVE_OPEN 1 + +/* Define to 1 if you have the `pipe' function. */ +#define HAVE_PIPE 1 + +/* Define to 1 if you have the `read' function. */ +#define HAVE_READ 1 + +/* Define to 1 if you have the `realloc' function. */ +#define HAVE_REALLOC 0 + +/* Define to 1 if you have the `setlocale' function. */ +#define HAVE_SETLOCALE 1 + +/* Set to 1 if is available. */ +#define HAVE_SNDIO_H 0 + +/* Define to 1 if you have the `snprintf' function. */ +#define HAVE_SNPRINTF 0 + +/* Set to 1 if you have libsqlite3. */ +#define HAVE_SQLITE3 1 + +/* Define to 1 if the system has the type `ssize_t'. */ +#define HAVE_SSIZE_T 0 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 0 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRINGS_H 0 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H 0 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_STAT_H 0 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TIME_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have that is POSIX.1 compatible. */ +#define HAVE_SYS_WAIT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_UNISTD_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_IMMINTRIN_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDBOOL_H 1 + +/* Define to 1 if you have the `vsnprintf' function. */ +#define HAVE_VSNPRINTF 0 + +/* Define to 1 if you have the `waitpid' function. */ +#define HAVE_WAITPID 1 + +/* Define to 1 if you have the `write' function. */ +#define HAVE_WRITE 1 + +/* The darwin version, no-zero is valid */ +#define OSX_DARWIN_VERSION 0 + +/* Set to 1 if compiling for OpenBSD */ +#define OS_IS_OPENBSD 0 + +/* Set to 1 if compiling for Win32 */ +#define OS_IS_WIN32 0 + +/* Set to 1 if SSE2 is enabled */ +/* #undef USE_SSE2 */ + +/* Name of package */ +#define PACKAGE "libsndfile" + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "libsndfile" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "libsndfile 1.0.31" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "libsndfile" + +/* Define to the home page for this package. */ +#define PACKAGE_URL "" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "1.0.31" + +/* Set to maximum allowed value of sf_count_t type. */ +#define SF_COUNT_MAX 0x7FFFFFFFFFFFFFFFLL + +/* The size of `double', as computed by sizeof. */ + + +/* The size of `float', as computed by sizeof. */ + + +/* The size of `int', as computed by sizeof. */ + + +/* The size of `int64_t', as computed by sizeof. */ +#define SIZEOF_INT64_T 8 + +/* The size of `loff_t', as computed by sizeof. */ + + +/* The size of `long', as computed by sizeof. */ +#define SIZEOF_LONG 8 + +/* The size of `long long', as computed by sizeof. */ +#define SIZEOF_LONG_LONG 8 + +/* The size of `off64_t', as computed by sizeof. */ + + +/* The size of `off_t', as computed by sizeof. */ +#define SIZEOF_OFF_T 8 + +/* Set to sizeof (long) if unknown. */ +#define SIZEOF_SF_COUNT_T 8 + +/* The size of `short', as computed by sizeof. */ + + +/* The size of `size_t', as computed by sizeof. */ + + +/* The size of `ssize_t', as computed by sizeof. */ +#define SIZEOF_SSIZE_T 8 + +/* The size of `void*', as computed by sizeof. */ + + +/* The size of `wchar_t', as computed by sizeof. */ +#define SIZEOF_WCHAR_T 4 + +/* Set to long if unknown. */ +#define TYPEOF_SF_COUNT_T int64_t + +/* Enable extensions on AIX 3, Interix. */ +#ifndef _ALL_SOURCE +# undef _ALL_SOURCE +#endif +/* Enable GNU extensions on systems that have them. */ +#ifndef _GNU_SOURCE +# undef _GNU_SOURCE +#endif +/* Enable threading extensions on Solaris. */ +#ifndef _POSIX_PTHREAD_SEMANTICS +# undef _POSIX_PTHREAD_SEMANTICS +#endif +/* Enable extensions on HP NonStop. */ +#ifndef _TANDEM_SOURCE +# undef _TANDEM_SOURCE +#endif +/* Enable general extensions on Solaris. */ +#ifndef __EXTENSIONS__ +# undef __EXTENSIONS__ +#endif + + +/* Set to 1 to use the native windows API */ +#define USE_WINDOWS_API 0 + +/* Version number of package */ +#define VERSION "1.0.31" + +/* Set to 1 if windows DLL is being built. */ +#define WIN32_TARGET_DLL 0 + +/* Target processor is big endian. */ +#define WORDS_BIGENDIAN 0 + +/* Enable large inode numbers on Mac OS X 10.5. */ +#ifndef _DARWIN_USE_64_BIT_INODE +# define _DARWIN_USE_64_BIT_INODE 1 +#endif + +/* Define to 1 if on MINIX. */ +#define _MINIX 0 + +/* Define as `__inline' or '__inline__' if that's what the C compiler calls it, or to nothing if it is not supported. */ +/* #undef inline */ diff --git a/frameworks/innerkitsimpl/libsnd/include/sndfile.h b/frameworks/innerkitsimpl/libsnd/include/sndfile.h new file mode 100755 index 0000000000000000000000000000000000000000..f0e65010e7e3db648d84193bfb0ecaea9bcf129c --- /dev/null +++ b/frameworks/innerkitsimpl/libsnd/include/sndfile.h @@ -0,0 +1,817 @@ +/* + * Copyright (C) 1999-2016 Erik de Castro Lopo + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +/* + * sndfile.h -- system-wide definitions + * + * API documentation is in the doc/ directory of the source code tarball + * and at http://libsndfile.github.io/libsndfile/api.html. +*/ + +#ifndef SNDFILE_H +#define SNDFILE_H + +/* This is the version 1.0.X header file. */ +#define SNDFILE_1 + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* The following file types can be read and written. + * A file type would consist of a major type (ie SF_FORMAT_WAV) bitwise + * ORed with a minor type (ie SF_FORMAT_PCM). SF_FORMAT_TYPEMASK and + * SF_FORMAT_SUBMASK can be used to separate the major and minor file + * types. +*/ + +enum { + /* Major formats. */ + SF_FORMAT_WAV = 0x010000, /* Microsoft WAV format (little endian default). */ + SF_FORMAT_AIFF = 0x020000, /* Apple/SGI AIFF format (big endian). */ + SF_FORMAT_AU = 0x030000, /* Sun/NeXT AU format (big endian). */ + SF_FORMAT_RAW = 0x040000, /* RAW PCM data. */ + SF_FORMAT_PAF = 0x050000, /* Ensoniq PARIS file format. */ + SF_FORMAT_SVX = 0x060000, /* Amiga IFF / SVX8 / SV16 format. */ + SF_FORMAT_NIST = 0x070000, /* Sphere NIST format. */ + SF_FORMAT_VOC = 0x080000, /* VOC files. */ + SF_FORMAT_IRCAM = 0x0A0000, /* Berkeley/IRCAM/CARL */ + SF_FORMAT_W64 = 0x0B0000, /* Sonic Foundry's 64 bit RIFF/WAV */ + SF_FORMAT_MAT4 = 0x0C0000, /* Matlab (tm) V4.2 / GNU Octave 2.0 */ + SF_FORMAT_MAT5 = 0x0D0000, /* Matlab (tm) V5.0 / GNU Octave 2.1 */ + SF_FORMAT_PVF = 0x0E0000, /* Portable Voice Format */ + SF_FORMAT_XI = 0x0F0000, /* Fasttracker 2 Extended Instrument */ + SF_FORMAT_HTK = 0x100000, /* HMM Tool Kit format */ + SF_FORMAT_SDS = 0x110000, /* Midi Sample Dump Standard */ + SF_FORMAT_AVR = 0x120000, /* Audio Visual Research */ + SF_FORMAT_WAVEX = 0x130000, /* MS WAVE with WAVEFORMATEX */ + SF_FORMAT_SD2 = 0x160000, /* Sound Designer 2 */ + SF_FORMAT_FLAC = 0x170000, /* FLAC lossless file format */ + SF_FORMAT_CAF = 0x180000, /* Core Audio File format */ + SF_FORMAT_WVE = 0x190000, /* Psion WVE format */ + SF_FORMAT_OGG = 0x200000, /* Xiph OGG container */ + SF_FORMAT_MPC2K = 0x210000, /* Akai MPC 2000 sampler */ + SF_FORMAT_RF64 = 0x220000, /* RF64 WAV file */ + SF_FORMAT_MPEG = 0x230000, /* MPEG-1/2 audio stream */ + /* Subtypes from here on. */ + SF_FORMAT_PCM_S8 = 0x0001, /* Signed 8 bit data */ + SF_FORMAT_PCM_16 = 0x0002, /* Signed 16 bit data */ + SF_FORMAT_PCM_24 = 0x0003, /* Signed 24 bit data */ + SF_FORMAT_PCM_32 = 0x0004, /* Signed 32 bit data */ + SF_FORMAT_PCM_U8 = 0x0005, /* Unsigned 8 bit data (WAV and RAW only) */ + SF_FORMAT_FLOAT = 0x0006, /* 32 bit float data */ + SF_FORMAT_DOUBLE = 0x0007, /* 64 bit float data */ + SF_FORMAT_ULAW = 0x0010, /* U-Law encoded. */ + SF_FORMAT_ALAW = 0x0011, /* A-Law encoded. */ + SF_FORMAT_IMA_ADPCM = 0x0012, /* IMA ADPCM. */ + SF_FORMAT_MS_ADPCM = 0x0013, /* Microsoft ADPCM. */ + SF_FORMAT_GSM610 = 0x0020, /* GSM 6.10 encoding. */ + SF_FORMAT_VOX_ADPCM = 0x0021, /* OKI / Dialogix ADPCM */ + SF_FORMAT_NMS_ADPCM_16 = 0x0022, /* 16kbs NMS G721-variant encoding. */ + SF_FORMAT_NMS_ADPCM_24 = 0x0023, /* 24kbs NMS G721-variant encoding. */ + SF_FORMAT_NMS_ADPCM_32 = 0x0024, /* 32kbs NMS G721-variant encoding. */ + SF_FORMAT_G721_32 = 0x0030, /* 32kbs G721 ADPCM encoding. */ + SF_FORMAT_G723_24 = 0x0031, /* 24kbs G723 ADPCM encoding. */ + SF_FORMAT_G723_40 = 0x0032, /* 40kbs G723 ADPCM encoding. */ + SF_FORMAT_DWVW_12 = 0x0040, /* 12 bit Delta Width Variable Word encoding. */ + SF_FORMAT_DWVW_16 = 0x0041, /* 16 bit Delta Width Variable Word encoding. */ + SF_FORMAT_DWVW_24 = 0x0042, /* 24 bit Delta Width Variable Word encoding. */ + SF_FORMAT_DWVW_N = 0x0043, /* N bit Delta Width Variable Word encoding. */ + SF_FORMAT_DPCM_8 = 0x0050, /* 8 bit differential PCM (XI only) */ + SF_FORMAT_DPCM_16 = 0x0051, /* 16 bit differential PCM (XI only) */ + SF_FORMAT_VORBIS = 0x0060, /* Xiph Vorbis encoding. */ + SF_FORMAT_OPUS = 0x0064, /* Xiph/Skype Opus encoding. */ + SF_FORMAT_ALAC_16 = 0x0070, /* Apple Lossless Audio Codec (16 bit). */ + SF_FORMAT_ALAC_20 = 0x0071, /* Apple Lossless Audio Codec (20 bit). */ + SF_FORMAT_ALAC_24 = 0x0072, /* Apple Lossless Audio Codec (24 bit). */ + SF_FORMAT_ALAC_32 = 0x0073, /* Apple Lossless Audio Codec (32 bit). */ + SF_FORMAT_MPEG_LAYER_I = 0x0080, /* MPEG-1 Audio Layer I */ + SF_FORMAT_MPEG_LAYER_II = 0x0081, /* MPEG-1 Audio Layer II */ + SF_FORMAT_MPEG_LAYER_III = 0x0082, /* MPEG-2 Audio Layer III */ + /* Endian-ness options. */ + SF_ENDIAN_FILE = 0x00000000, /* Default file endian-ness. */ + SF_ENDIAN_LITTLE = 0x10000000, /* Force little endian-ness. */ + SF_ENDIAN_BIG = 0x20000000, /* Force big endian-ness. */ + SF_ENDIAN_CPU = 0x30000000, /* Force CPU endian-ness. */ + SF_FORMAT_SUBMASK = 0x0000FFFF, + SF_FORMAT_TYPEMASK = 0x0FFF0000, + SF_FORMAT_ENDMASK = 0x30000000 +}; + +/* + * The following are the valid command numbers for the sf_command() + * interface. The use of these commands is documented in the file + * command.html in the doc directory of the source code distribution. +*/ +enum { + SFC_GET_LIB_VERSION = 0x1000, + SFC_GET_LOG_INFO = 0x1001, + SFC_GET_CURRENT_SF_INFO = 0x1002, + SFC_GET_NORM_DOUBLE = 0x1010, + SFC_GET_NORM_FLOAT = 0x1011, + SFC_SET_NORM_DOUBLE = 0x1012, + SFC_SET_NORM_FLOAT = 0x1013, + SFC_SET_SCALE_FLOAT_INT_READ = 0x1014, + SFC_SET_SCALE_INT_FLOAT_WRITE = 0x1015, + SFC_GET_SIMPLE_FORMAT_COUNT = 0x1020, + SFC_GET_SIMPLE_FORMAT = 0x1021, + SFC_GET_FORMAT_INFO = 0x1028, + SFC_GET_FORMAT_MAJOR_COUNT = 0x1030, + SFC_GET_FORMAT_MAJOR = 0x1031, + SFC_GET_FORMAT_SUBTYPE_COUNT = 0x1032, + SFC_GET_FORMAT_SUBTYPE = 0x1033, + SFC_CALC_SIGNAL_MAX = 0x1040, + SFC_CALC_NORM_SIGNAL_MAX = 0x1041, + SFC_CALC_MAX_ALL_CHANNELS = 0x1042, + SFC_CALC_NORM_MAX_ALL_CHANNELS = 0x1043, + SFC_GET_SIGNAL_MAX = 0x1044, + SFC_GET_MAX_ALL_CHANNELS = 0x1045, + SFC_SET_ADD_PEAK_CHUNK = 0x1050, + SFC_UPDATE_HEADER_NOW = 0x1060, + SFC_SET_UPDATE_HEADER_AUTO = 0x1061, + SFC_FILE_TRUNCATE = 0x1080, + SFC_SET_RAW_START_OFFSET = 0x1090, + /* Commands reserved for dithering, which is not implemented. */ + SFC_SET_DITHER_ON_WRITE = 0x10A0, + SFC_SET_DITHER_ON_READ = 0x10A1, + SFC_GET_DITHER_INFO_COUNT = 0x10A2, + SFC_GET_DITHER_INFO = 0x10A3, + SFC_GET_EMBED_FILE_INFO = 0x10B0, + SFC_SET_CLIPPING = 0x10C0, + SFC_GET_CLIPPING = 0x10C1, + SFC_GET_CUE_COUNT = 0x10CD, + SFC_GET_CUE = 0x10CE, + SFC_SET_CUE = 0x10CF, + SFC_GET_INSTRUMENT = 0x10D0, + SFC_SET_INSTRUMENT = 0x10D1, + SFC_GET_LOOP_INFO = 0x10E0, + SFC_GET_BROADCAST_INFO = 0x10F0, + SFC_SET_BROADCAST_INFO = 0x10F1, + SFC_GET_CHANNEL_MAP_INFO = 0x1100, + SFC_SET_CHANNEL_MAP_INFO = 0x1101, + SFC_RAW_DATA_NEEDS_ENDSWAP = 0x1110, + /* Support for Wavex Ambisonics Format */ + SFC_WAVEX_SET_AMBISONIC = 0x1200, + SFC_WAVEX_GET_AMBISONIC = 0x1201, + /* + * RF64 files can be set so that on-close, writable files that have less + * than 4GB of data in them are converted to RIFF/WAV, as per EBU + * recommendations. + */ + SFC_RF64_AUTO_DOWNGRADE = 0x1210, + SFC_SET_VBR_ENCODING_QUALITY = 0x1300, + SFC_SET_COMPRESSION_LEVEL = 0x1301, + SFC_SET_OGG_PAGE_LATENCY_MS = 0x1302, + SFC_SET_OGG_PAGE_LATENCY = 0x1303, + SFC_GET_BITRATE_MODE = 0x1302, + SFC_SET_BITRATE_MODE = 0x1303, + /* Cart Chunk support */ + SFC_SET_CART_INFO = 0x1400, + SFC_GET_CART_INFO = 0x1401, + /* Opus files original samplerate metadata */ + SFC_SET_ORIGINAL_SAMPLERATE = 0x1500, + SFC_GET_ORIGINAL_SAMPLERATE = 0x1501, + /* Following commands for testing only. */ + SFC_TEST_IEEE_FLOAT_REPLACE = 0x6001, + /* + * These SFC_SET_ADD_* values are deprecated and will disappear at some + * time in the future. They are guaranteed to be here up to and + * including version 1.0.8 to avoid breakage of existing software. + * They currently do nothing and will continue to do nothing. + */ + SFC_SET_ADD_HEADER_PAD_CHUNK = 0x1051, + SFC_SET_ADD_DITHER_ON_WRITE = 0x1070, + SFC_SET_ADD_DITHER_ON_READ = 0x1071 +}; + + /* + * String types that can be set and read from files. Not all file types + * support this and even the file types which support one, may not support + * all string types. + */ +enum { + SF_STR_TITLE = 0x01, + SF_STR_COPYRIGHT = 0x02, + SF_STR_SOFTWARE = 0x03, + SF_STR_ARTIST = 0x04, + SF_STR_COMMENT = 0x05, + SF_STR_DATE = 0x06, + SF_STR_ALBUM = 0x07, + SF_STR_LICENSE = 0x08, + SF_STR_TRACKNUMBER = 0x09, + SF_STR_GENRE = 0x10 +}; + +/* + * Use the following as the start and end index when doing metadata + * transcoding. +*/ + +#define SF_STR_FIRST SF_STR_TITLE +#define SF_STR_LAST SF_STR_GENRE + +enum { + /* True and false */ + SF_FALSE = 0, + SF_TRUE = 1, + /* Modes for opening files. */ + SFM_READ = 0x10, + SFM_WRITE = 0x20, + SFM_RDWR = 0x30, + + SF_AMBISONIC_NONE = 0x40, + SF_AMBISONIC_B_FORMAT = 0x41 +}; + +/* Public error values. These are guaranteed to remain unchanged for the duration + * of the library major version number. + * There are also a large number of private error numbers which are internal to + * the library which can change at any time. +*/ + +enum { + SF_ERR_NO_ERROR = 0, + SF_ERR_UNRECOGNISED_FORMAT = 1, + SF_ERR_SYSTEM = 2, + SF_ERR_MALFORMED_FILE = 3, + SF_ERR_UNSUPPORTED_ENCODING = 4 +}; + + +/* Channel map values (used with SFC_SET/GET_CHANNEL_MAP). +*/ + +enum { + SF_CHANNEL_MAP_INVALID = 0, + SF_CHANNEL_MAP_MONO = 1, + SF_CHANNEL_MAP_LEFT, /* Apple calls this 'Left' */ + SF_CHANNEL_MAP_RIGHT, /* Apple calls this 'Right' */ + SF_CHANNEL_MAP_CENTER, /* Apple calls this 'Center' */ + SF_CHANNEL_MAP_FRONT_LEFT, + SF_CHANNEL_MAP_FRONT_RIGHT, + SF_CHANNEL_MAP_FRONT_CENTER, + SF_CHANNEL_MAP_REAR_CENTER, /* Apple calls this 'Center Surround', Msft calls this 'Back Center' */ + SF_CHANNEL_MAP_REAR_LEFT, /* Apple calls this 'Left Surround', Msft calls this 'Back Left' */ + SF_CHANNEL_MAP_REAR_RIGHT, /* Apple calls this 'Right Surround', Msft calls this 'Back Right' */ + SF_CHANNEL_MAP_LFE, /* Apple calls this 'LFEScreen', Msft calls this 'Low Frequency' */ + SF_CHANNEL_MAP_FRONT_LEFT_OF_CENTER, /* Apple calls this 'Left Center' */ + SF_CHANNEL_MAP_FRONT_RIGHT_OF_CENTER, /* Apple calls this 'Right Center */ + SF_CHANNEL_MAP_SIDE_LEFT, /* Apple calls this 'Left Surround Direct' */ + SF_CHANNEL_MAP_SIDE_RIGHT, /* Apple calls this 'Right Surround Direct' */ + SF_CHANNEL_MAP_TOP_CENTER, /* Apple calls this 'Top Center Surround' */ + SF_CHANNEL_MAP_TOP_FRONT_LEFT, /* Apple calls this 'Vertical Height Left' */ + SF_CHANNEL_MAP_TOP_FRONT_RIGHT, /* Apple calls this 'Vertical Height Right' */ + SF_CHANNEL_MAP_TOP_FRONT_CENTER, /* Apple calls this 'Vertical Height Center' */ + SF_CHANNEL_MAP_TOP_REAR_LEFT, /* Apple and MS call this 'Top Back Left' */ + SF_CHANNEL_MAP_TOP_REAR_RIGHT, /* Apple and MS call this 'Top Back Right' */ + SF_CHANNEL_MAP_TOP_REAR_CENTER, /* Apple and MS call this 'Top Back Center' */ + SF_CHANNEL_MAP_AMBISONIC_B_W, + SF_CHANNEL_MAP_AMBISONIC_B_X, + SF_CHANNEL_MAP_AMBISONIC_B_Y, + SF_CHANNEL_MAP_AMBISONIC_B_Z, + SF_CHANNEL_MAP_MAX +}; + +/* Bitrate mode values (for use with SFC_GET/SET_BITRATE_MODE) +*/ +enum { + SF_BITRATE_MODE_CONSTANT = 0, + SF_BITRATE_MODE_AVERAGE, + SF_BITRATE_MODE_VARIABLE +}; + + +/* A SNDFILE* pointer can be passed around much like stdio.h's FILE* pointer. */ + +typedef struct sf_private_tag SNDFILE; + +/* The following typedef is system specific and is defined when libsndfile is + * compiled. sf_count_t will be a 64 bit value when the underlying OS allows + * 64 bit file offsets. + * On windows, we need to allow the same header file to be compiler by both GCC + * and the Microsoft compiler. +*/ + +typedef int64_t sf_count_t; +#ifndef SF_COUNT_MAX +#define SF_COUNT_MAX 0x7FFFFFFFFFFFFFFFLL +#endif + + +/* A pointer to a SF_INFO structure is passed to sf_open () and filled in. + * On write, the SF_INFO structure is filled in by the user and passed into + * sf_open (). +*/ + +struct SF_INFO { + sf_count_t frames; /* Used to be called samples. Changed to avoid confusion. */ + int samplerate; + int channels; + int format; + int sections; + int seekable; +}; + +typedef struct SF_INFO SF_INFO; + +/* The SF_FORMAT_INFO struct is used to retrieve information about the sound + * file formats libsndfile supports using the sf_command () interface. + * + * Using this interface will allow applications to support new file formats + * and encoding types when libsndfile is upgraded, without requiring + * re-compilation of the application. + * + * Please consult the libsndfile documentation (particularly the information + * on the sf_command () interface) for examples of its use. +*/ + +typedef struct { + int format; + const char *name; + const char *extension; +} SF_FORMAT_INFO; + +/* + * Enums and typedefs for adding dither on read and write. + * Reserved for future implementation. +*/ + +enum { + SFD_DEFAULT_LEVEL = 0, + SFD_CUSTOM_LEVEL = 0x40000000, + SFD_NO_DITHER = 500, + SFD_WHITE = 501, + SFD_TRIANGULAR_PDF = 502 +}; + +typedef struct { + int type; + double level; + const char *name; +} SF_DITHER_INFO; + + /* Struct used to retrieve information about a file embedded within a + * larger file. See SFC_GET_EMBED_FILE_INFO. + */ + +typedef struct { + sf_count_t offset; + sf_count_t length; +} SF_EMBED_FILE_INFO; + +/* + * Struct used to retrieve cue marker information from a file +*/ + +typedef struct { + int32_t indx; + uint32_t position; + int32_t fcc_chunk; + int32_t chunk_start; + int32_t block_start; + uint32_t sample_offset; + char name [256]; +} SF_CUE_POINT; + +#define SF_CUES_VAR(count) \ + struct \ + { \ + uint32_t cue_count; \ + SF_CUE_POINT cue_points [count]; \ + } + +typedef SF_CUES_VAR (100) SF_CUES; + +/* + * Structs used to retrieve music sample information from a file. +*/ + +enum { + /* + * The loop mode field in SF_INSTRUMENT will be one of the following. + */ + SF_LOOP_NONE = 800, + SF_LOOP_FORWARD, + SF_LOOP_BACKWARD, + SF_LOOP_ALTERNATING +}; + +typedef struct { + int gain; + char basenote, detune; + char velocity_lo, velocity_hi; + char key_lo, key_hi; + int loop_count; + + struct { + int mode; + uint32_t start; + uint32_t end; + uint32_t count; + } loops [16]; /* make variable in a sensible way */ +} SF_INSTRUMENT; + +/* Struct used to retrieve loop information from a file. */ +typedef struct { + short time_sig_num; /* any positive integer > 0 */ + short time_sig_den; /* any positive power of 2 > 0 */ + int loop_mode; /* see SF_LOOP enum */ + + int num_beats; /* this is NOT the amount of quarter notes !!! */ + /* a full bar of 4/4 is 4 beats */ + /* a full bar of 7/8 is 7 beats */ + + float bpm; /* suggestion, as it can be calculated using other fields: */ + /* file's length, file's sampleRate and our time_sig_den */ + /* -> bpms are always the amount of _quarter notes_ per minute */ + + int root_key; /* MIDI note, or -1 for None */ + int future [6]; +} SF_LOOP_INFO; + + +/* Struct used to retrieve broadcast (EBU) information from a file. + * Strongly (!) based on EBU "bext" chunk format used in Broadcast WAVE. +*/ +#define SF_BROADCAST_INFO_VAR(coding_hist_size) \ + struct \ + { \ + char description [256]; \ + char originator [32]; \ + char originator_reference [32]; \ + char origination_date [10]; \ + char origination_time [8]; \ + uint32_t time_reference_low; \ + uint32_t time_reference_high; \ + short version; \ + char umid [64]; \ + int16_t loudness_value; \ + int16_t loudness_range; \ + int16_t max_true_peak_level; \ + int16_t max_momentary_loudness; \ + int16_t max_shortterm_loudness; \ + char reserved [180]; \ + uint32_t coding_history_size; \ + char coding_history [coding_hist_size]; \ + } + +/* SF_BROADCAST_INFO is the above struct with coding_history field of 256 bytes. */ +typedef SF_BROADCAST_INFO_VAR (256) SF_BROADCAST_INFO; + +struct SF_CART_TIMER { + char usage [4]; + int32_t value; +}; + +typedef struct SF_CART_TIMER SF_CART_TIMER; + +#define SF_CART_INFO_VAR(p_tag_text_size) \ + struct \ + { \ + char version [4]; \ + char title [64]; \ + char artist [64]; \ + char cut_id [64]; \ + char client_id [64]; \ + char category [64]; \ + char classification [64]; \ + char out_cue [64]; \ + char start_date [10]; \ + char start_time [8]; \ + char end_date [10]; \ + char end_time [8]; \ + char producer_app_id [64]; \ + char producer_app_version [64]; \ + char user_def [64]; \ + int32_t level_reference; \ + SF_CART_TIMER post_timers [8]; \ + char reserved [276]; \ + char url [1024]; \ + uint32_t tag_text_size; \ + char tag_text [p_tag_text_size]; \ + } + +typedef SF_CART_INFO_VAR (256) SF_CART_INFO; + +/* Virtual I/O functionality. */ + +typedef sf_count_t (*sf_vio_get_filelen) (void *user_data); +typedef sf_count_t (*sf_vio_seek) (sf_count_t offset, int whence, void *user_data); +typedef sf_count_t (*sf_vio_read) (void *ptr, sf_count_t count, void *user_data); +typedef sf_count_t (*sf_vio_write) (const void *ptr, sf_count_t count, void *user_data); +typedef sf_count_t (*sf_vio_tell) (void *user_data); + +struct SF_VIRTUAL_IO { + sf_vio_get_filelen get_filelen; + sf_vio_seek seek; + sf_vio_read read; + sf_vio_write write; + sf_vio_tell tell; +}; + +typedef struct SF_VIRTUAL_IO SF_VIRTUAL_IO; + + +/* Open the specified file for read, write or both. On error, this will + * return a NULL pointer. To find the error number, pass a NULL SNDFILE + * to sf_strerror (). + * All calls to sf_open() should be matched with a call to sf_close(). +*/ + +SNDFILE* sf_open (const char *path, int mode, SF_INFO *sfinfo); + + +/* Use the existing file descriptor to create a SNDFILE object. If close_desc + * is TRUE, the file descriptor will be closed when sf_close() is called. If + * it is FALSE, the descriptor will not be closed. + * When passed a descriptor like this, the library will assume that the start + * of file header is at the current file offset. This allows sound files within + * larger container files to be read and/or written. + * On error, this will return a NULL pointer. To find the error number, pass a + * NULL SNDFILE to sf_strerror (). + * All calls to sf_open_fd() should be matched with a call to sf_close(). + +*/ + +SNDFILE* sf_open_fd (int fd, int mode, SF_INFO *sfinfo, int close_desc); + +SNDFILE* sf_open_virtual (SF_VIRTUAL_IO *sfvirtual, int mode, SF_INFO *sfinfo, void *user_data); + + +/* sf_error () returns a error number which can be translated to a text + * string using sf_error_number(). +*/ + +int sf_error (SNDFILE *sndfile); + + +/* sf_strerror () returns to the caller a pointer to the current error message for + * the given SNDFILE. +*/ + +const char* sf_strerror (SNDFILE *sndfile); + + +/* sf_error_number () allows the retrieval of the error string for each internal + * error number. + * +*/ + +const char* sf_error_number (int errnum); + + +/* The following two error functions are deprecated but they will remain in the + * library for the foreseeable future. The function sf_strerror() should be used + * in their place. +*/ + +int sf_perror (SNDFILE *sndfile); +int sf_error_str (SNDFILE *sndfile, char* str, size_t len); + + +/* Allow the caller to retrieve information from or change aspects of the + * library behaviour. +*/ + +int sf_command (SNDFILE *sndfile, int command, void *data, int datasize); + + +/* Return TRUE if fields of the SF_INFO struct are a valid combination of values. */ + +int sf_format_check (const SF_INFO *info); + + +/* Seek within the waveform data chunk of the SNDFILE. sf_seek () uses + * the same values for whence (SEEK_SET, SEEK_CUR and SEEK_END) as + * stdio.h function fseek (). + * An offset of zero with whence set to SEEK_SET will position the + * read / write pointer to the first data sample. + * On success sf_seek returns the current position in (multi-channel) + * samples from the start of the file. + * Please see the libsndfile documentation for moving the read pointer + * separately from the write pointer on files open in mode SFM_RDWR. + * On error all of these functions return -1. +*/ + +enum { + SF_SEEK_SET = SEEK_SET, + SF_SEEK_CUR = SEEK_CUR, + SF_SEEK_END = SEEK_END +}; + +sf_count_t sf_seek (SNDFILE *sndfile, sf_count_t frames, int whence); + +/* Functions for retrieving and setting string data within sound files. + * Not all file types support this features; AIFF and WAV do. For both + * functions, the str_type parameter must be one of the SF_STR_* values + * defined above. + * On error, sf_set_string() returns non-zero while sf_get_string() + * returns NULL. +*/ + +int sf_set_string (SNDFILE *sndfile, int str_type, const char* str); + +const char* sf_get_string (SNDFILE *sndfile, int str_type); + + +/* Return the library version string. */ + +const char * sf_version_string (void); + +/* Return the current byterate at this point in the file. The byte rate in this + * case is the number of bytes per second of audio data. For instance, for a + * stereo, 18 bit PCM encoded file with an 16kHz sample rate, the byte rate + * would be 2 (stereo) * 2 (two bytes per sample) * 16000 => 64000 bytes/sec. + * For some file formats the returned value will be accurate and exact, for some + * it will be a close approximation, for some it will be the average bitrate for + * the whole file and for some it will be a time varying value that was accurate + * when the file was most recently read or written. + * To get the bitrate, multiple this value by 8. + * Returns -1 for unknown. +*/ +int sf_current_byterate (SNDFILE *sndfile); + +/* Functions for reading/writing the waveform data of a sound file. +*/ + +sf_count_t sf_read_raw (SNDFILE *sndfile, void *ptr, sf_count_t bytes); +sf_count_t sf_write_raw (SNDFILE *sndfile, const void *ptr, sf_count_t bytes); + +/* Functions for reading and writing the data chunk in terms of frames. + * The number of items actually read/written = frames * number of channels. + * sf_xxxx_raw read/writes the raw data bytes from/to the file + * sf_xxxx_short passes data in the native short format + * sf_xxxx_int passes data in the native int format + * sf_xxxx_float passes data in the native float format + * sf_xxxx_double passes data in the native double format + * All of these read/write function return number of frames read/written. +*/ + +sf_count_t sf_readf_short (SNDFILE *sndfile, short *ptr, sf_count_t frames); +sf_count_t sf_writef_short (SNDFILE *sndfile, const short *ptr, sf_count_t frames); + +sf_count_t sf_readf_int (SNDFILE *sndfile, int *ptr, sf_count_t frames); +sf_count_t sf_writef_int (SNDFILE *sndfile, const int *ptr, sf_count_t frames); + +sf_count_t sf_readf_float (SNDFILE *sndfile, float *ptr, sf_count_t frames); +sf_count_t sf_writef_float (SNDFILE *sndfile, const float *ptr, sf_count_t frames); + +sf_count_t sf_readf_double (SNDFILE *sndfile, double *ptr, sf_count_t frames); +sf_count_t sf_writef_double (SNDFILE *sndfile, const double *ptr, sf_count_t frames); + + +/* Functions for reading and writing the data chunk in terms of items. + * Otherwise similar to above. + * All of these read/write function return number of items read/written. +*/ + +sf_count_t sf_read_short (SNDFILE *sndfile, short *ptr, sf_count_t items); +sf_count_t sf_write_short (SNDFILE *sndfile, const short *ptr, sf_count_t items); + +sf_count_t sf_read_int (SNDFILE *sndfile, int *ptr, sf_count_t items); +sf_count_t sf_write_int (SNDFILE *sndfile, const int *ptr, sf_count_t items); + +sf_count_t sf_read_float (SNDFILE *sndfile, float *ptr, sf_count_t items); +sf_count_t sf_write_float (SNDFILE *sndfile, const float *ptr, sf_count_t items); + +sf_count_t sf_read_double (SNDFILE *sndfile, double *ptr, sf_count_t items); +sf_count_t sf_write_double (SNDFILE *sndfile, const double *ptr, sf_count_t items); + + +/* Close the SNDFILE and clean up all memory allocations associated with this + * file. + * Returns 0 on success, or an error number. +*/ + +int sf_close (SNDFILE *sndfile); + + +/* If the file is opened SFM_WRITE or SFM_RDWR, call fsync() on the file + * to force the writing of data to disk. If the file is opened SFM_READ + * no action is taken. +*/ + +void sf_write_sync (SNDFILE *sndfile); + +/* The function sf_wchar_open() is Windows Only! + * Open a file passing in a Windows Unicode filename. Otherwise, this is + * the same as sf_open(). +*/ + +#ifdef _WIN32 +SNDFILE* sf_wchar_open (const wchar_t *wpath, int mode, SF_INFO *sfinfo); +#endif + +/* Getting and setting of chunks from within a sound file. + * + * These functions allow the getting and setting of chunks within a sound file + * (for those formats which allow it). + * + * These functions fail safely. Specifically, they will not allow you to overwrite + * existing chunks or add extra versions of format specific reserved chunks but + * should allow you to retrieve any and all chunks (may not be implemented for + * all chunks or all file formats). +*/ + +struct SF_CHUNK_INFO { + char id [64]; /* The chunk identifier. */ + unsigned id_size; /* The size of the chunk identifier. */ + unsigned datalen; /* The size of that data. */ + void *data; /* Pointer to the data. */ +}; + +typedef struct SF_CHUNK_INFO SF_CHUNK_INFO; + +/* Set the specified chunk info (must be done before any audio data is written + * to the file). This will fail for format specific reserved chunks. + * The chunk_info->data pointer must be valid until the file is closed. + * Returns SF_ERR_NO_ERROR on success or non-zero on failure. +*/ +int sf_set_chunk (SNDFILE * sndfile, const SF_CHUNK_INFO * chunk_info); + +/* + * An opaque structure to an iterator over the all chunks of a given id +*/ +typedef struct SF_CHUNK_ITERATOR SF_CHUNK_ITERATOR; + +/* Get an iterator for all chunks matching chunk_info. + * The iterator will point to the first chunk matching chunk_info. + * Chunks are matching, if (chunk_info->id) matches the first + * (chunk_info->id_size) bytes of a chunk found in the SNDFILE* handle. + * If chunk_info is NULL, an iterator to all chunks in the SNDFILE* handle + * is returned. + * The values of chunk_info->datalen and chunk_info->data are ignored. + * If no matching chunks are found in the sndfile, NULL is returned. + * The returned iterator will stay valid until one of the following occurs + * a) The sndfile is closed. + * b) A new chunk is added using sf_set_chunk(). + * c) Another chunk iterator function is called on the same SNDFILE* handle + * that causes the iterator to be modified. + * The memory for the iterator belongs to the SNDFILE* handle and is freed when + * sf_close() is called. +*/ +SF_CHUNK_ITERATOR * sf_get_chunk_iterator (SNDFILE * sndfile, const SF_CHUNK_INFO * chunk_info); + +/* Iterate through chunks by incrementing the iterator. + * Increments the iterator and returns a handle to the new one. + * After this call, iterator will no longer be valid, and you must use the + * newly returned handle from now on. + * The returned handle can be used to access the next chunk matching + * the criteria as defined in sf_get_chunk_iterator(). + * If iterator points to the last chunk, this will free all resources + * associated with iterator and return NULL. + * The returned iterator will stay valid until sf_get_chunk_iterator_next + * is called again, the sndfile is closed or a new chunk us added. +*/ +SF_CHUNK_ITERATOR * sf_next_chunk_iterator (SF_CHUNK_ITERATOR * iterator); + +/* Get the size of the specified chunk. + * If the specified chunk exists, the size will be returned in the + * datalen field of the SF_CHUNK_INFO struct. + * Additionally, the id of the chunk will be copied to the id + * field of the SF_CHUNK_INFO struct and it's id_size field will + * be updated accordingly. + * If the chunk doesn't exist chunk_info->datalen will be zero, and the + * id and id_size fields will be undefined. + * The function will return SF_ERR_NO_ERROR on success or non-zero on + * failure. +*/ +int sf_get_chunk_size (const SF_CHUNK_ITERATOR * it, SF_CHUNK_INFO * chunk_info); + +/* Get the specified chunk data. + * If the specified chunk exists, up to chunk_info->datalen bytes of + * the chunk data will be copied into the chunk_info->data buffer + * (allocated by the caller) and the chunk_info->datalen field + * updated to reflect the size of the data. The id and id_size + * field will be updated according to the retrieved chunk + * If the chunk doesn't exist chunk_info->datalen will be zero, and the + * id and id_size fields will be undefined. + * The function will return SF_ERR_NO_ERROR on success or non-zero on + * failure. +*/ +int sf_get_chunk_data (const SF_CHUNK_ITERATOR * it, SF_CHUNK_INFO * chunk_info); + +#ifdef __cplusplus +} /* extern "C" */ +#endif /* __cplusplus */ + +#endif /* SNDFILE_H */ diff --git a/frameworks/innerkitsimpl/pulseaudio/BUILD.gn b/frameworks/innerkitsimpl/pulseaudio/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..86bfbf60f1fa03f2b831234c8a273258bd633804 --- /dev/null +++ b/frameworks/innerkitsimpl/pulseaudio/BUILD.gn @@ -0,0 +1,50 @@ +# Copyright (C) 2021 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") + +pulseaudio_build_path = "//foundation/multimedia/audio_standard/frameworks/innerkitsimpl/pulseaudio" +libsndfile_build_path = "//foundation/multimedia/audio_standard/frameworks/innerkitsimpl/libsnd" + +group("pulseaudio_packages") { + deps = [ + # ":gen_config_header", + "$libsndfile_build_path:sndfile", + "$pulseaudio_build_path/src:pulsecommon", + "$pulseaudio_build_path/src/pulse:pulse", + "$pulseaudio_build_path/src:pa_daemon_config", + "$pulseaudio_build_path/src:pa_default_config", + "$pulseaudio_build_path/src:pa_client_config", + "$pulseaudio_build_path/src/pulse:pulse-simple", + "$pulseaudio_build_path/src/pulse:pulse-mainloop-glib", + "$pulseaudio_build_path/src/pulsecore:pulsecore", + "$pulseaudio_build_path/src/pulsecore:protocol-native", + "$pulseaudio_build_path/src/pulsecore:cli", + "$pulseaudio_build_path/src/pulsecore:protocol-cli", + "$pulseaudio_build_path/src/daemon:pulseaudio", + "$pulseaudio_build_path/src/utils:pacmd", + "$pulseaudio_build_path/src/utils:pacat", + "$pulseaudio_build_path/src/utils:pactl", + "$pulseaudio_build_path/src/modules:modules", + "$pulseaudio_build_path/src/modules/hdi:module-hdi-sink", + "$pulseaudio_build_path/src/modules/hdi:module-hdi-source", + ] +} + +action("gen_config_header") { + script = "$pulseaudio_build_path/ohos_paconfig.sh" + + args = [ rebase_path("//third_party/pulseaudio", root_build_dir), rebase_path( "${target_gen_dir}/", root_build_dir),] + + outputs = [ "${target_gen_dir}/config.h", ] +} diff --git a/frameworks/innerkitsimpl/pulseaudio/conf/client.conf b/frameworks/innerkitsimpl/pulseaudio/conf/client.conf new file mode 100644 index 0000000000000000000000000000000000000000..9e40ae269bcde0baf52051925c5574e6774c9a13 --- /dev/null +++ b/frameworks/innerkitsimpl/pulseaudio/conf/client.conf @@ -0,0 +1,20 @@ +# This file is part of PulseAudio. +# +# PulseAudio is free software; you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# PulseAudio is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with PulseAudio; if not, see . + +## Configuration file for PulseAudio clients. See pulse-client.conf(5) for +## more information. Default values are commented out. Use either ; or # for +## commenting. +cookie-file = /data/local/.pulse_dir/cookie + diff --git a/frameworks/innerkitsimpl/pulseaudio/conf/daemon.conf b/frameworks/innerkitsimpl/pulseaudio/conf/daemon.conf new file mode 100644 index 0000000000000000000000000000000000000000..8bcaf7cda669773c21155936fd2bb94024f9b69f --- /dev/null +++ b/frameworks/innerkitsimpl/pulseaudio/conf/daemon.conf @@ -0,0 +1,91 @@ +# This file is part of PulseAudio. +# +# PulseAudio is free software; you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# PulseAudio is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with PulseAudio; if not, see . + +## Configuration file for the PulseAudio daemon. See pulse-daemon.conf(5) for +## more information. Default values are commented out. Use either ; or # for +## commenting. + +; daemonize = no +; fail = yes +; allow-module-loading = yes +; disallow-exit = yes +disallow-exit = yes +allow-exit = no +; use-pid-file = yes +; system-instance = no +; local-server-type = user +; enable-shm = yes +; enable-memfd = yes +; shm-size-bytes = 0 # setting this 0 will use the system-default, usually 64 MiB +; lock-memory = no +; cpu-limit = no + +; high-priority = yes +; nice-level = -11 + +; realtime-scheduling = yes +; realtime-priority = 5 + +exit-idle-time = -1 +; scache-idle-time = 20 + +; dl-search-path = (depends on architecture) + +; load-default-script-file = yes +; default-script-file = + +; log-target = auto +; log-level = notice +; log-meta = no +; log-time = no +; log-backtrace = 0 + +; resample-method = speex-float-1 +; avoid-resampling = false +; enable-remixing = yes +; remixing-use-all-sink-channels = yes +; enable-lfe-remixing = no +; lfe-crossover-freq = 0 + +flat-volumes = no + +; rlimit-fsize = -1 +; rlimit-data = -1 +; rlimit-stack = -1 +; rlimit-core = -1 +; rlimit-as = -1 +; rlimit-rss = -1 +; rlimit-nproc = -1 +; rlimit-nofile = 256 +; rlimit-memlock = -1 +; rlimit-locks = -1 +; rlimit-sigpending = -1 +; rlimit-msgqueue = -1 +; rlimit-nice = 31 +; rlimit-rtprio = 9 +; rlimit-rttime = 200000 + +; default-sample-format = s16le +; default-sample-rate = 44100 +; alternate-sample-rate = 48000 +; default-sample-channels = 2 +; default-channel-map = front-left,front-right + +; default-fragments = 4 +; default-fragment-size-msec = 25 + +; enable-deferred-volume = yes +deferred-volume-safety-margin-usec = 1 +; deferred-volume-extra-delay-usec = 0 diff --git a/frameworks/innerkitsimpl/pulseaudio/conf/default.pa b/frameworks/innerkitsimpl/pulseaudio/conf/default.pa new file mode 100644 index 0000000000000000000000000000000000000000..1f08228fb4926b3093c179fa8751d098f17d059a --- /dev/null +++ b/frameworks/innerkitsimpl/pulseaudio/conf/default.pa @@ -0,0 +1,34 @@ +#!/usr/bin/pulseaudio -nF +# +# This file is part of PulseAudio. +# +# PulseAudio is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# PulseAudio is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with PulseAudio; if not, see . + +# This startup script is used only if PulseAudio is started per-user +# (i.e. not in system mode) + +.fail + +# Load mandatory modules +load-module libmodule-cli-protocol-unix.z.so +load-module libmodule-native-protocol-unix.z.so auth-cookie=/data/local/.pulse_dir/cookie + +### Load audio drivers statically +### (it's probably better to not load these drivers manually, but instead +### use module-udev-detect -- see below -- for doing this automatically) +load-module libmodule-suspend-on-idle.z.so timeout=1 + +### Make some devices default +#set-default-sink output +#set-default-source input diff --git a/frameworks/innerkitsimpl/pulseaudio/include/config.h b/frameworks/innerkitsimpl/pulseaudio/include/config.h new file mode 100644 index 0000000000000000000000000000000000000000..219aee0cedac6071fc119df28c1458a3ef4e7c57 --- /dev/null +++ b/frameworks/innerkitsimpl/pulseaudio/include/config.h @@ -0,0 +1,285 @@ +/* + * Autogenerated by the Meson build system. + * Do not edit, your changes will be lost. + */ + +#pragma once + +#define DESKTOPFILEDIR "/usr/local/share/applications" + +#define DISABLE_ORC 1 + +#define ENABLE_LEGACY_DATABASE_ENTRY_FORMAT 1 + +#define GETGROUPS_T gid_t + +#define GETTEXT_PACKAGE "pulseaudio" + +#define HAVE_ACCEPT4 1 + +#define HAVE_ALSA 1 + +#define HAVE_ALSA_UCM 1 + +#define HAVE_ARPA_INET_H 1 + +#define HAVE_ATOMIC_BUILTINS 1 + +#define HAVE_ATOMIC_BUILTINS_MEMORY_MODEL 1 + +#define HAVE_BYTESWAP_H 1 + +#define HAVE_CLOCK_GETTIME 1 + +#define HAVE_COREAUDIO 0 + +#define HAVE_CTIME_R 1 + +#define HAVE_DECL_ENVIRON 1 + +#define HAVE_DECL_SOUND_PCM_READ_BITS 1 + +#define HAVE_DECL_SOUND_PCM_READ_CHANNELS 1 + +#define HAVE_DECL_SOUND_PCM_READ_RATE 1 + +#define HAVE_DLADDR 1 + +#define HAVE_DLFCN_H 1 + +#define HAVE_FAST_64BIT_OPERATIONS 1 + +#define HAVE_FCHMOD 1 + +#define HAVE_FCHOWN 1 + +#define HAVE_FORK 1 + +#define HAVE_FSTAT 1 + +#define HAVE_GETADDRINFO 1 + +#define HAVE_GETGRGID_R 1 + +#define HAVE_GETGRNAM_R 1 + +#define HAVE_GETPWNAM_R 1 + +#define HAVE_GETPWUID_R 1 + +#define HAVE_GETTIMEOFDAY 1 + +#define HAVE_GETUID 1 + +#define HAVE_GRP_H 1 + +#define HAVE_HAL_COMPAT 1 + +#define HAVE_ICONV 1 + +#define HAVE_IPV6 1 + +#define HAVE_LANGINFO_H 1 + +#define HAVE_LINUX_SOCKIOS_H 1 + +#define HAVE_LOCALE_H 1 + +#define HAVE_LRINTF 1 + +#define HAVE_LSTAT 1 + +#define HAVE_MEMFD_CREATE 1 + +#define HAVE_MKFIFO 1 + +#define HAVE_MLOCK 1 + +/* Compiler supports mmx. */ +#define HAVE_MMX 1 + +#define HAVE_NANOSLEEP 1 + +#define HAVE_NETDB_H 1 + +#define HAVE_NETINET_IN_H 1 + +#define HAVE_NETINET_IN_SYSTM_H 1 + +#define HAVE_NETINET_IP_H 1 + +#define HAVE_NETINET_TCP_H 1 + +#define HAVE_OPEN64 1 + +#define HAVE_OPENSSL 1 + +#define HAVE_OSS_OUTPUT 1 + +#define HAVE_OSS_WRAPPER 1 + +#define HAVE_PCREPOSIX_H 1 + +#define HAVE_PIPE 1 + +#define HAVE_PIPE2 1 + +#define HAVE_POLL_H 1 + +#define HAVE_POSIX_FADVISE 1 + +#define HAVE_POSIX_MADVISE 1 + +#define HAVE_POSIX_MEMALIGN 1 + +#define HAVE_PPOLL 1 + +#define HAVE_PTHREAD 1 + +#define HAVE_PTHREAD_GETNAME_NP 1 + +#define HAVE_PTHREAD_PRIO_INHERIT 1 + +#define HAVE_PTHREAD_SETAFFINITY_NP 1 + +#define HAVE_PTHREAD_SETNAME_NP 1 + +#define HAVE_PWD_H 1 + +#define HAVE_READLINK 1 + +#define HAVE_REGEX_H 1 + +#define HAVE_RUNNING_FROM_BUILD_TREE 1 + +#define HAVE_SCHED_H 1 + +#define HAVE_SETEGID 1 + +#define HAVE_SETEUID 1 + +#define HAVE_SETPGID 1 + +#define HAVE_SETREGID 1 + +#define HAVE_SETRESGID 1 + +#define HAVE_SETRESUID 1 + +#define HAVE_SETREUID 1 + +#define HAVE_SETSID 1 + +#define HAVE_SIGACTION 1 + +#define HAVE_SIGXCPU 1 + +/* Compiler supports sse. */ +#define HAVE_SSE 1 + +#define HAVE_STDINT_H 1 + +#define HAVE_STD_BOOL 1 + +#define HAVE_STRERROR_R 1 + +#define HAVE_STRTOD_L 1 + +#define HAVE_STRTOF 1 + +#define HAVE_SYMLINK 1 + +#define HAVE_SYSCONF 1 + +#define HAVE_SYSLOG_H 1 + +#define HAVE_SYS_EVENTFD_H 1 + +#define HAVE_SYS_IOCTL_H 1 + +#define HAVE_SYS_MMAN_H 1 + +#define HAVE_SYS_PRCTL_H 1 + +#define HAVE_SYS_RESOURCE_H 1 + +#define HAVE_SYS_SELECT_H 1 + +#define HAVE_SYS_SOCKET_H 1 + +#define HAVE_SYS_SYSCALL_H 1 + +#define HAVE_SYS_UIO_H 1 + +#define HAVE_SYS_UN_H 1 + +#define HAVE_SYS_WAIT_H 1 + +#define HAVE_UNAME 1 + +#define HAVE_WAVEOUT 0 + +#define ICONV_CONST + +#define LIBICONV_PLUG 1 + +#define MESON_BUILD 1 + +#define PACKAGE "pulseaudio" + +#define PACKAGE_NAME "pulseaudio" + +#define PACKAGE_VERSION "14.0-271-g1a19" + +#define PA_ACCESS_GROUP "pulse-access" + +#define PA_ALSA_PATHS_DIR "/usr/local/share/pulseaudio/alsa-mixer/paths" + +#define PA_ALSA_PROFILE_SETS_DIR "/usr/local/share/pulseaudio/alsa-mixer/profile-sets" + +#define PA_API_VERSION 12 + +#define PA_BINARY "/system/bin" + +#define PA_BUILDDIR "/home/workspace/pa/pulseaudio/confgure" + +#define PA_CFLAGS "Not yet supported on meson" + +#define PA_DEFAULT_CONFIG_DIR "/system/etc/pulse" + +#define PA_DEFAULT_CONFIG_DIR_UNQUOTED /usr/local/etc/pulse + +#define PA_DLSEARCHPATH "/system/lib" + +#define PA_INCDIR /usr/local/include + +#define PA_LIBDIR /usr/local/lib/x86_64-linux-gnu + +#define PA_MACHINE_ID "/usr/local/etc/machine-id" + +#define PA_MACHINE_ID_FALLBACK "/var/local/lib/dbus/machine-id" + +#define PA_MAJOR 14 + +#define PA_MINOR 0 + +#define PA_PROTOCOL_VERSION 35 + +#define PA_SOEXT ".so" + +#define PA_SYSTEM_CONFIG_PATH "/var/local/lib/pulse" + +#define PA_SYSTEM_GROUP "pulse" + +#define PA_SYSTEM_RUNTIME_PATH "/var/local/run/pulse" + +#define PA_SYSTEM_STATE_PATH "/var/local/lib/pulse" + +#define PA_SYSTEM_USER "pulse" + +#define PULSEDSP_LOCATION /usr/local/lib/x86_64-linux-gnu/pulseaudio + +#define PULSE_LOCALEDIR "/usr/local/share/locale" + +#define top_srcdir /home/workspace/pa/pulseaudio + diff --git a/frameworks/innerkitsimpl/pulseaudio/include/ltdl.h b/frameworks/innerkitsimpl/pulseaudio/include/ltdl.h new file mode 100644 index 0000000000000000000000000000000000000000..9ce4b7d3ea355c1df5f3a3581c70c5a515f2ff23 --- /dev/null +++ b/frameworks/innerkitsimpl/pulseaudio/include/ltdl.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2021 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. + */ + +/* This file stubs the functionality of + * libtool library used by pulseaudio. + */ + +#include + +typedef void* lt_dlhandle; + +const char *lt_dlerror(void); +const char* lt_dlgetsearchpath(); +int lt_dlclose(lt_dlhandle handle); +lt_dlhandle lt_dlopenext(const char *filename); +void* lt_dlsym(lt_dlhandle handle, const char *symbol); diff --git a/frameworks/innerkitsimpl/pulseaudio/include/pulse/version.h b/frameworks/innerkitsimpl/pulseaudio/include/pulse/version.h new file mode 100644 index 0000000000000000000000000000000000000000..b4fda9325c5ed573e7d2cf967b0d7a787309dbb1 --- /dev/null +++ b/frameworks/innerkitsimpl/pulseaudio/include/pulse/version.h @@ -0,0 +1,70 @@ +#ifndef fooversionhfoo /*-*-C-*-*/ +#define fooversionhfoo + +/*** + This file is part of PulseAudio. + + Copyright 2004-2006 Lennart Poettering + Copyright 2006 Pierre Ossman for Cendio AB + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, see . +***/ + +/* WARNING: Make sure to edit the real source file version.h.in! */ + +#include + +/** \file + * Define header version */ + +PA_C_DECL_BEGIN + +/** Return the version of the header files. Keep in mind that this is +a macro and not a function, so it is impossible to get the pointer of +it. */ +#define pa_get_headers_version() ("14.0.0") + +/** Return the version of the library the current application is + * linked to. */ +const char* pa_get_library_version(void); + +/** The current API version. Version 6 relates to Polypaudio + * 0.6. Prior versions (i.e. Polypaudio 0.5.1 and older) have + * PA_API_VERSION undefined. Please note that this is only ever + * increased on incompatible API changes! */ +#define PA_API_VERSION 12 + +/** The current protocol version. Version 8 relates to Polypaudio + * 0.8/PulseAudio 0.9. */ +#define PA_PROTOCOL_VERSION 35 + +/** The major version of PA. \since 0.9.15 */ +#define PA_MAJOR 14 + +/** The minor version of PA. \since 0.9.15 */ +#define PA_MINOR 0 + +/** The micro version of PA (will always be 0 from v1.0 onwards). \since 0.9.15 */ +#define PA_MICRO 0 + +/** Evaluates to TRUE if the PulseAudio library version is equal or + * newer than the specified. \since 0.9.16 */ +#define PA_CHECK_VERSION(major,minor,micro) \ + ((PA_MAJOR > (major)) || \ + (PA_MAJOR == (major) && PA_MINOR > (minor)) || \ + (PA_MAJOR == (major) && PA_MINOR == (minor) && PA_MICRO >= (micro))) + +PA_C_DECL_END + +#endif diff --git a/frameworks/innerkitsimpl/pulseaudio/ohos_paconfig.sh b/frameworks/innerkitsimpl/pulseaudio/ohos_paconfig.sh new file mode 100644 index 0000000000000000000000000000000000000000..cca462f023afe27c3274e0a0a05f67ac1356e66b --- /dev/null +++ b/frameworks/innerkitsimpl/pulseaudio/ohos_paconfig.sh @@ -0,0 +1,75 @@ +# Copyright (C) 2021 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. + +#!/bin/bash + +PASRC_PATH=$1 +PASRC_OUT_PATH=$2 + +function clean_pa_src() { + echo "Clean local generated files" + rm -rf ABOUT-NLS Makefile Makefile.in PulseAudioConfig.cmake PulseAudioConfigVersion.cmake aclocal.m4 + rm -rf autom4te.cache/ build-aux/ config.h config.h.in config.h.in~ config.log config.status configure + rm -rf libpulse-mainloop-glib.pc libpulse-simple.pc libpulse.pc libtool stamp-h1 +} + +clean_pa_src + +sed -i 's/\.\/git-version-gen .tarball-version//g' configure.ac +sed -i 's/\[m4_esyscmd()\],//g' configure.ac +sed -i 's/src doxygen man po/src doxygen man #po/g' Makefile.am + +PA_CONFIG_OPTIONS=" + --without-caps + --disable-alsa + --disable-x11 + --disable-oss-output + --disable-coreaudio-output + --disable-alsa + --disable-esound + --disable-gsettings + --disable-dbus + --disable-udev + --disable-ipv6 + --disable-openssl + --disable-avahi + --disable-jack +" +# We check for this here, because if pkg-config is not found in the +# system, it's likely that the pkg.m4 macro file is also not present, +# which will make PKG_PROG_PKG_CONFIG be undefined and the generated +# configure file faulty. +if ! pkg-config --version &>/dev/null; then + echo "pkg-config is required to bootstrap this program" + +fi +# Other necessary programs +if ! autopoint --version &>/dev/null ; then + echo "autopoint is required to bootstrap this program" + +fi + +autoreconf --force --install --verbose + +if test "x$NOCONFIGURE" = "x"; then + CFLAGS="$CFLAGS -g -O0" $PASRC_PATH/configure --enable-force-preopen ${PA_CONFIG_OPTIONS} && \ + make clean +fi + +sed -i 's/#define ENABLE_NLS 1//g' config.h +sed -i 's/#define HAVE_SHM_OPEN 1//g' config.h +sed -i 's/#define HAVE_RUNNING_FROM_BUILD_TREE 1//g' config.h +sed -i 's/#define HAVE_CPUID_H 1//g' config.h +sed -i 's/#define HAVE_EXECINFO_H 1//g' config.h +sed -i 's/#define HAVE_MEMFD 1//g' config.h +echo "#define PACKAGE_NAME \"pulseaudio\"" >> config.h diff --git a/frameworks/innerkitsimpl/pulseaudio/src/BUILD.gn b/frameworks/innerkitsimpl/pulseaudio/src/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..da8fac3376cf8f0a72865103e672a11046c78420 --- /dev/null +++ b/frameworks/innerkitsimpl/pulseaudio/src/BUILD.gn @@ -0,0 +1,150 @@ +# Copyright (C) 2021 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") + +pulseaudio_dir = "//third_party/pulseaudio" +pulseaudio_build_path = "//foundation/multimedia/audio_standard/frameworks/innerkitsimpl/pulseaudio" +libsndfile_build_path = "//foundation/multimedia/audio_standard/frameworks/innerkitsimpl/libsnd" + +config("pulsecommon_config") { + visibility = [ ":*" ] + + include_dirs = [ + "$pulseaudio_dir/src", + "$pulseaudio_dir/include", + "$pulseaudio_dir", + "$pulseaudio_build_path/src", + "$pulseaudio_build_path/include", + "$libsndfile_build_path/include", + "$pulseaudio_dir/src/pulse", + ] + + cflags = [ + "-Wall", + "-Werror", + "-Wno-implicit-function-declaration", + "-Wno-unused-function", + "-DHAVE_CONFIG_H", + "-D_GNU_SOURCE", + ] +} + +ohos_source_set("pulsecommon_sources") { + sources = [ + "$pulseaudio_dir/src/pulse/client-conf.c", + "$pulseaudio_dir/src/pulse/error.c", + "$pulseaudio_dir/src/pulse/fork-detect.c", + "$pulseaudio_dir/src/pulse/format.c", + "$pulseaudio_dir/src/pulse/json.c", + "$pulseaudio_dir/src/pulse/mainloop-api.c", + "$pulseaudio_dir/src/pulse/xmalloc.c", + "$pulseaudio_dir/src/pulse/proplist.c", + "$pulseaudio_dir/src/pulse/utf8.c", + "$pulseaudio_dir/src/pulse/channelmap.c", + "$pulseaudio_dir/src/pulse/sample.c", + "$pulseaudio_dir/src/pulse/util.c", + "$pulseaudio_dir/src/pulse/timeval.c", + "$pulseaudio_dir/src/pulse/rtclock.c", + "$pulseaudio_build_path/src/pulse/ohos_pa_volume.c", + "$pulseaudio_dir/src/pulsecore/authkey.c", + "$pulseaudio_dir/src/pulsecore/conf-parser.c", + "$pulseaudio_dir/src/pulsecore/core-error.c", + "$pulseaudio_dir/src/pulsecore/core-format.c", + "$pulseaudio_dir/src/pulsecore/core-rtclock.c", + "$pulseaudio_dir/src/pulsecore/core-util.c", + "$pulseaudio_dir/src/pulsecore/dynarray.c", + "$pulseaudio_dir/src/pulsecore/fdsem.c", + "$pulseaudio_dir/src/pulsecore/flist.c", + "$pulseaudio_dir/src/pulsecore/g711.c", + "$pulseaudio_dir/src/pulsecore/hashmap.c", + "$pulseaudio_dir/src/pulsecore/i18n.c", + "$pulseaudio_dir/src/pulsecore/idxset.c", + "$pulseaudio_dir/src/pulsecore/arpa-inet.c", + "$pulseaudio_dir/src/pulsecore/iochannel.c", + "$pulseaudio_dir/src/pulsecore/ioline.c", + "$pulseaudio_dir/src/pulsecore/ipacl.c", + "$pulseaudio_dir/src/pulsecore/lock-autospawn.c", + "$pulseaudio_dir/src/pulsecore/log.c", + "$pulseaudio_dir/src/pulsecore/ratelimit.c", + "$pulseaudio_dir/src/pulsecore/mcalign.c", + "$pulseaudio_dir/src/pulsecore/memblock.c", + "$pulseaudio_dir/src/pulsecore/memblockq.c", + "$pulseaudio_dir/src/pulsecore/memchunk.c", + "$pulseaudio_dir/src/pulsecore/native-common.c", + "$pulseaudio_dir/src/pulsecore/once.c", + "$pulseaudio_dir/src/pulsecore/packet.c", + "$pulseaudio_dir/src/pulsecore/parseaddr.c", + "$pulseaudio_dir/src/pulsecore/pdispatch.c", + "$pulseaudio_dir/src/pulsecore/pid.c", + "$pulseaudio_dir/src/pulsecore/pipe.c", + "$pulseaudio_dir/src/pulsecore/memtrap.c", + "$pulseaudio_dir/src/pulsecore/aupdate.c", + "$pulseaudio_dir/src/pulsecore/proplist-util.c", + "$pulseaudio_dir/src/pulsecore/pstream-util.c", + "$pulseaudio_dir/src/pulsecore/pstream.c", + "$pulseaudio_dir/src/pulsecore/queue.c", + "$pulseaudio_dir/src/pulsecore/random.c", + "$pulseaudio_dir/src/pulsecore/srbchannel.c", + "$pulseaudio_dir/src/pulsecore/sample-util.c", + "$pulseaudio_dir/src/pulsecore/shm.c", + "$pulseaudio_dir/src/pulsecore/bitset.c", + "$pulseaudio_dir/src/pulsecore/socket-client.c", + "$pulseaudio_dir/src/pulsecore/socket-server.c", + "$pulseaudio_dir/src/pulsecore/socket-util.c", + "$pulseaudio_dir/src/pulsecore/strbuf.c", + "$pulseaudio_dir/src/pulsecore/strlist.c", + "$pulseaudio_dir/src/pulsecore/tagstruct.c", + "$pulseaudio_dir/src/pulsecore/time-smoother.c", + "$pulseaudio_dir/src/pulsecore/tokenizer.c", + "$pulseaudio_dir/src/pulsecore/usergroup.c", + "$pulseaudio_dir/src/pulsecore/sndfile-util.c", + "$pulseaudio_dir/src/pulsecore/mutex-posix.c", + "$pulseaudio_dir/src/pulsecore/semaphore-posix.c", + "$pulseaudio_dir/src/pulsecore/thread-posix.c", + ] + + configs = [ ":pulsecommon_config" ] +} + +ohos_shared_library("pulsecommon") { + deps = [ + ":pulsecommon_sources", + "$libsndfile_build_path:sndfile", + ] + + subsystem_name = "multimedia" + part_name = "multimedia_audio_standard" +} +ohos_prebuilt_etc("pa_daemon_config") { + source = "$pulseaudio_build_path/conf/daemon.conf" + + subsystem_name = "multimedia" + module_install_dir = "etc/pulse" + part_name = "multimedia_audio_standard" +} + +ohos_prebuilt_etc("pa_default_config") { + source = "$pulseaudio_build_path/conf/default.pa" + subsystem_name = "multimedia" + module_install_dir = "etc/pulse" + part_name = "multimedia_audio_standard" +} + +ohos_prebuilt_etc("pa_client_config") { + source = "$pulseaudio_build_path/conf/client.conf" + subsystem_name = "multimedia" + module_install_dir = "etc/pulse" + part_name = "multimedia_audio_standard" +} + diff --git a/frameworks/innerkitsimpl/pulseaudio/src/daemon/BUILD.gn b/frameworks/innerkitsimpl/pulseaudio/src/daemon/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..89868e9edb9fd10c2a6e256a7a5f200f070b0e1c --- /dev/null +++ b/frameworks/innerkitsimpl/pulseaudio/src/daemon/BUILD.gn @@ -0,0 +1,61 @@ +# Copyright (C) 2021 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") + +pulseaudio_build_path = "//foundation/multimedia/audio_standard/frameworks/innerkitsimpl/pulseaudio" +pulseaudio_dir = "//third_party/pulseaudio" + +config("daemon_config") { + visibility = [ ":*" ] + + include_dirs = [ + "$pulseaudio_build_path/include", + "$pulseaudio_dir/src/daemon", + "$pulseaudio_dir/src", + "$pulseaudio_dir", + ] + + cflags = [ + "-Wall", + "-Werror", + "-Wno-unused-function", + "-DHAVE_CONFIG_H", + "-DHAVE_UNISTD_H", + ] +} + +ohos_source_set("pulseaudio_sources") { + sources = [ + "$pulseaudio_dir/src/daemon/caps.c", + "$pulseaudio_dir/src/daemon/cmdline.c", + "$pulseaudio_dir/src/daemon/cpulimit.c", + "$pulseaudio_build_path/src/daemon/ohos_daemon-conf.c", + "$pulseaudio_build_path/src/daemon/ohos_pa_main.c", + ] + + configs = [ ":daemon_config" ] +} + +ohos_shared_library("pulseaudio") { + ldflags = [ "-ffast-math" ] + deps = [ + ":pulseaudio_sources", + "$pulseaudio_build_path/src/pulse:pulse", + "$pulseaudio_build_path/src/pulsecore:pulsecore", + "$pulseaudio_build_path/src:pulsecommon", + ] + + part_name = "multimedia_audio_standard" + subsystem_name = "multimedia" +} diff --git a/frameworks/innerkitsimpl/pulseaudio/src/daemon/ohos_daemon-conf.c b/frameworks/innerkitsimpl/pulseaudio/src/daemon/ohos_daemon-conf.c new file mode 100644 index 0000000000000000000000000000000000000000..4a6fe0b3d37e756cf0634953a799de29c5804e56 --- /dev/null +++ b/frameworks/innerkitsimpl/pulseaudio/src/daemon/ohos_daemon-conf.c @@ -0,0 +1,872 @@ +/*** + This file is part of PulseAudio. + + Copyright 2004-2006 Lennart Poettering + Copyright 2006 Pierre Ossman for Cendio AB + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, see . +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_SCHED_H +#include +#endif + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "daemon-conf.h" + +#define DEFAULT_SCRIPT_FILE PA_DEFAULT_CONFIG_DIR PA_PATH_SEP "default.pa" +#define DEFAULT_SCRIPT_FILE_USER PA_PATH_SEP "default.pa" +#define DEFAULT_SYSTEM_SCRIPT_FILE PA_DEFAULT_CONFIG_DIR PA_PATH_SEP "system.pa" + +#define DEFAULT_CONFIG_FILE PA_DEFAULT_CONFIG_DIR PA_PATH_SEP "daemon.conf" +#define DEFAULT_CONFIG_FILE_USER PA_PATH_SEP "daemon.conf" + +#define ENV_SCRIPT_FILE "PULSE_SCRIPT" +#define ENV_CONFIG_FILE "PULSE_CONFIG" +#define ENV_DL_SEARCH_PATH "PULSE_DLPATH" + +static const pa_daemon_conf default_conf = { + .cmd = PA_CMD_DAEMON, + .daemonize = false, + .fail = true, + .high_priority = true, + .nice_level = -11, + .realtime_scheduling = true, + .realtime_priority = 5, /* Half of JACK's default rtprio */ + .disallow_module_loading = false, + .disallow_exit = false, + .flat_volumes = false, + .rescue_streams = true, + .exit_idle_time = -1, + .scache_idle_time = 20, + .script_commands = NULL, + .dl_search_path = NULL, + .load_default_script_file = true, + .default_script_file = NULL, + .log_target = NULL, + .log_level = PA_LOG_NOTICE, + .log_backtrace = 0, + .log_meta = false, + .log_time = false, + .resample_method = PA_RESAMPLER_AUTO, + .avoid_resampling = false, + .disable_remixing = false, + .remixing_use_all_sink_channels = true, + .remixing_produce_lfe = false, + .remixing_consume_lfe = false, + .lfe_crossover_freq = 0, + .config_file = NULL, + .use_pid_file = true, + .system_instance = false, +#ifdef HAVE_DBUS + .local_server_type = PA_SERVER_TYPE_UNSET, /* The actual default is _USER, but we have to detect when the user doesn't specify this option. */ +#endif + .no_cpu_limit = true, + .disable_shm = false, + .disable_memfd = false, + .lock_memory = false, + .deferred_volume = true, + .default_n_fragments = 4, + .default_fragment_size_msec = 25, + .deferred_volume_safety_margin_usec = 8000, + .deferred_volume_extra_delay_usec = 0, + .default_sample_spec = { .format = PA_SAMPLE_S16NE, .rate = 44100, .channels = 2 }, + .alternate_sample_rate = 48000, + .default_channel_map = { .channels = 2, .map = { PA_CHANNEL_POSITION_LEFT, PA_CHANNEL_POSITION_RIGHT } }, + .shm_size = 0 +#ifdef HAVE_SYS_RESOURCE_H + ,.rlimit_fsize = { .value = 0, .is_set = false }, + .rlimit_data = { .value = 0, .is_set = false }, + .rlimit_stack = { .value = 0, .is_set = false }, + .rlimit_core = { .value = 0, .is_set = false } +#ifdef RLIMIT_RSS + ,.rlimit_rss = { .value = 0, .is_set = false } +#endif +#ifdef RLIMIT_NPROC + ,.rlimit_nproc = { .value = 0, .is_set = false } +#endif +#ifdef RLIMIT_NOFILE + ,.rlimit_nofile = { .value = 256, .is_set = true } +#endif +#ifdef RLIMIT_MEMLOCK + ,.rlimit_memlock = { .value = 0, .is_set = false } +#endif +#ifdef RLIMIT_AS + ,.rlimit_as = { .value = 0, .is_set = false } +#endif +#ifdef RLIMIT_LOCKS + ,.rlimit_locks = { .value = 0, .is_set = false } +#endif +#ifdef RLIMIT_SIGPENDING + ,.rlimit_sigpending = { .value = 0, .is_set = false } +#endif +#ifdef RLIMIT_MSGQUEUE + ,.rlimit_msgqueue = { .value = 0, .is_set = false } +#endif +#ifdef RLIMIT_NICE + ,.rlimit_nice = { .value = 31, .is_set = true } /* nice level of -11 */ +#endif +#ifdef RLIMIT_RTPRIO + ,.rlimit_rtprio = { .value = 9, .is_set = true } /* One below JACK's default for the server */ +#endif +#ifdef RLIMIT_RTTIME + ,.rlimit_rttime = { .value = 200*PA_USEC_PER_MSEC, .is_set = true } /* rtkit's limit is 200 ms */ +#endif +#endif +}; + +pa_daemon_conf *pa_daemon_conf_new(void) { + pa_daemon_conf *c; + + c = pa_xnewdup(pa_daemon_conf, &default_conf, 1); + +#ifdef OS_IS_WIN32 + c->dl_search_path = pa_sprintf_malloc("%s" PA_PATH_SEP "lib" PA_PATH_SEP "pulse-%d.%d" PA_PATH_SEP "modules", + pa_win32_get_toplevel(NULL), PA_MAJOR, PA_MINOR); +#else +#ifdef HAVE_RUNNING_FROM_BUILD_TREE + if (pa_run_from_build_tree()) { + pa_log_notice("Detected that we are run from the build tree, fixing search path."); +#ifdef MESON_BUILD + c->dl_search_path = pa_xstrdup(PA_BUILDDIR PA_PATH_SEP "src" PA_PATH_SEP "modules"); +#else + c->dl_search_path = pa_xstrdup(PA_BUILDDIR); +#endif // Endof #ifdef MESON_BUILD + } else +#endif // Endof #ifdef HAVE_RUNNING_FROM_BUILD_TREE + c->dl_search_path = pa_xstrdup(PA_DLSEARCHPATH); +#endif // Endof #ifdef OS_IS_WIN32 + + return c; +} + +void pa_daemon_conf_free(pa_daemon_conf *c) { + pa_assert(c); + + pa_xfree(c->script_commands); + pa_xfree(c->dl_search_path); + pa_xfree(c->default_script_file); + + if (c->log_target) + pa_log_target_free(c->log_target); + + pa_xfree(c->config_file); + pa_xfree(c); +} + +int pa_daemon_conf_set_log_target(pa_daemon_conf *c, const char *string) { + pa_log_target *log_target = NULL; + + pa_assert(c); + pa_assert(string); + + if (!pa_streq(string, "auto")) { + log_target = pa_log_parse_target(string); + + if (!log_target) + return -1; + } + + c->log_target = log_target; + + return 0; +} + +int pa_daemon_conf_set_log_level(pa_daemon_conf *c, const char *string) { + uint32_t u; + pa_assert(c); + pa_assert(string); + + if (pa_atou(string, &u) >= 0) { + if (u >= PA_LOG_LEVEL_MAX) + return -1; + + c->log_level = (pa_log_level_t) u; + } else if (pa_startswith(string, "debug")) + c->log_level = PA_LOG_DEBUG; + else if (pa_startswith(string, "info")) + c->log_level = PA_LOG_INFO; + else if (pa_startswith(string, "notice")) + c->log_level = PA_LOG_NOTICE; + else if (pa_startswith(string, "warn")) + c->log_level = PA_LOG_WARN; + else if (pa_startswith(string, "err")) + c->log_level = PA_LOG_ERROR; + else + return -1; + + return 0; +} + +int pa_daemon_conf_set_resample_method(pa_daemon_conf *c, const char *string) { + int m; + pa_assert(c); + pa_assert(string); + + if ((m = pa_parse_resample_method(string)) < 0) + return -1; + + c->resample_method = m; + return 0; +} + +int pa_daemon_conf_set_local_server_type(pa_daemon_conf *c, const char *string) { + pa_assert(c); + pa_assert(string); + + if (pa_streq(string, "user")) + c->local_server_type = PA_SERVER_TYPE_USER; + else if (pa_streq(string, "system")) { + c->local_server_type = PA_SERVER_TYPE_SYSTEM; + } else if (pa_streq(string, "none")) { + c->local_server_type = PA_SERVER_TYPE_NONE; + } else + return -1; + + return 0; +} + +static int parse_log_target(pa_config_parser_state *state) { + pa_daemon_conf *c; + + pa_assert(state); + + c = state->data; + + if (pa_daemon_conf_set_log_target(c, state->rvalue) < 0) { + pa_log(_("[%s:%u] Invalid log target '%s'."), state->filename, state->lineno, state->rvalue); + return -1; + } + + return 0; +} + +static int parse_log_level(pa_config_parser_state *state) { + pa_daemon_conf *c; + + pa_assert(state); + + c = state->data; + + if (pa_daemon_conf_set_log_level(c, state->rvalue) < 0) { + pa_log(_("[%s:%u] Invalid log level '%s'."), state->filename, state->lineno, state->rvalue); + return -1; + } + + return 0; +} + +static int parse_resample_method(pa_config_parser_state *state) { + pa_daemon_conf *c; + + pa_assert(state); + + c = state->data; + + if (pa_daemon_conf_set_resample_method(c, state->rvalue) < 0) { + pa_log(_("[%s:%u] Invalid resample method '%s'."), state->filename, state->lineno, state->rvalue); + return -1; + } + + return 0; +} + +#ifdef HAVE_SYS_RESOURCE_H +static int parse_rlimit(pa_config_parser_state *state) { + struct pa_rlimit *r; + + pa_assert(state); + + r = state->data; + + if (state->rvalue[strspn(state->rvalue, "\t ")] == 0) { + /* Empty string */ + r->is_set = 0; + r->value = 0; + } else { + int32_t k; + if (pa_atoi(state->rvalue, &k) < 0) { + pa_log(_("[%s:%u] Invalid rlimit '%s'."), state->filename, state->lineno, state->rvalue); + return -1; + } + r->is_set = k >= 0; + r->value = k >= 0 ? (rlim_t) k : 0; + } + + return 0; +} +#endif + +static int parse_sample_format(pa_config_parser_state *state) { + pa_daemon_conf *c; + pa_sample_format_t f; + + pa_assert(state); + + c = state->data; + + if ((f = pa_parse_sample_format(state->rvalue)) < 0) { + pa_log(_("[%s:%u] Invalid sample format '%s'."), state->filename, state->lineno, state->rvalue); + return -1; + } + + c->default_sample_spec.format = f; + return 0; +} + +static int parse_sample_rate(pa_config_parser_state *state) { + pa_daemon_conf *c; + uint32_t r; + + pa_assert(state); + + c = state->data; + + if (pa_atou(state->rvalue, &r) < 0 || !pa_sample_rate_valid(r)) { + pa_log(_("[%s:%u] Invalid sample rate '%s'."), state->filename, state->lineno, state->rvalue); + return -1; + } + + c->default_sample_spec.rate = r; + return 0; +} + +static int parse_alternate_sample_rate(pa_config_parser_state *state) { + pa_daemon_conf *c; + uint32_t r; + + pa_assert(state); + + c = state->data; + + if (pa_atou(state->rvalue, &r) < 0 || !pa_sample_rate_valid(r)) { + pa_log(_("[%s:%u] Invalid sample rate '%s'."), state->filename, state->lineno, state->rvalue); + return -1; + } + + c->alternate_sample_rate = r; + return 0; +} + +struct channel_conf_info { + pa_daemon_conf *conf; + bool default_sample_spec_set; + bool default_channel_map_set; +}; + +static int parse_sample_channels(pa_config_parser_state *state) { + struct channel_conf_info *i; + int32_t n; + + pa_assert(state); + + i = state->data; + + if (pa_atoi(state->rvalue, &n) < 0 || !pa_channels_valid(n)) { + pa_log(_("[%s:%u] Invalid sample channels '%s'."), state->filename, state->lineno, state->rvalue); + return -1; + } + + i->conf->default_sample_spec.channels = (uint8_t) n; + i->default_sample_spec_set = true; + return 0; +} + +static int parse_channel_map(pa_config_parser_state *state) { + struct channel_conf_info *i; + + pa_assert(state); + + i = state->data; + + if (!pa_channel_map_parse(&i->conf->default_channel_map, state->rvalue)) { + pa_log(_("[%s:%u] Invalid channel map '%s'."), state->filename, state->lineno, state->rvalue); + return -1; + } + + i->default_channel_map_set = true; + return 0; +} + +static int parse_fragments(pa_config_parser_state *state) { + pa_daemon_conf *c; + int32_t n; + + pa_assert(state); + + c = state->data; + + if (pa_atoi(state->rvalue, &n) < 0 || n < 2) { + pa_log(_("[%s:%u] Invalid number of fragments '%s'."), state->filename, state->lineno, state->rvalue); + return -1; + } + + c->default_n_fragments = (unsigned) n; + return 0; +} + +static int parse_fragment_size_msec(pa_config_parser_state *state) { + pa_daemon_conf *c; + int32_t n; + + pa_assert(state); + + c = state->data; + + if (pa_atoi(state->rvalue, &n) < 0 || n < 1) { + pa_log(_("[%s:%u] Invalid fragment size '%s'."), state->filename, state->lineno, state->rvalue); + return -1; + } + + c->default_fragment_size_msec = (unsigned) n; + return 0; +} + +static int parse_nice_level(pa_config_parser_state *state) { + pa_daemon_conf *c; + int32_t level; + + pa_assert(state); + + c = state->data; + + if (pa_atoi(state->rvalue, &level) < 0 || level < -20 || level > 19) { + pa_log(_("[%s:%u] Invalid nice level '%s'."), state->filename, state->lineno, state->rvalue); + return -1; + } + + c->nice_level = (int) level; + return 0; +} + +static int parse_rtprio(pa_config_parser_state *state) { +#if !defined(OS_IS_WIN32) && defined(HAVE_SCHED_H) + pa_daemon_conf *c; + int32_t rtprio; +#endif + + pa_assert(state); + +#ifdef OS_IS_WIN32 + pa_log("[%s:%u] Realtime priority not available on win32.", state->filename, state->lineno); +#else +# ifdef HAVE_SCHED_H + c = state->data; + + if (pa_atoi(state->rvalue, &rtprio) < 0 || rtprio < sched_get_priority_min(SCHED_FIFO) || rtprio > sched_get_priority_max(SCHED_FIFO)) { + pa_log("[%s:%u] Invalid realtime priority '%s'.", state->filename, state->lineno, state->rvalue); + return -1; + } + + c->realtime_priority = (int) rtprio; +# endif +#endif /* OS_IS_WIN32 */ + + return 0; +} + +static int parse_disable_lfe_remix(pa_config_parser_state *state) { + pa_daemon_conf *c; + int k; + + pa_assert(state); + c = state->data; + + if ((k = pa_parse_boolean(state->rvalue)) < 0) { + pa_log("[%s:%u] Failed to parse boolean value: %s", state->filename, state->lineno, state->rvalue); + return -1; + } + + c->remixing_produce_lfe = c->remixing_consume_lfe = !k; + + pa_log("[%s:%u] Deprecated option 'disable-lfe-remixing' found.", state->filename, state->lineno); + pa_log("[%s:%u] Please migrate to 'remixing-produce-lfe' and 'remixing-consume-lfe', set both to '%s'.", + state->filename, state->lineno, pa_yes_no(c->remixing_produce_lfe)); + + return 0; +} + +static int parse_enable_lfe_remix(pa_config_parser_state *state) { + pa_daemon_conf *c; + int k; + + pa_assert(state); + c = state->data; + + if ((k = pa_parse_boolean(state->rvalue)) < 0) { + pa_log("[%s:%u] Failed to parse boolean value: %s", state->filename, state->lineno, state->rvalue); + return -1; + } + + c->remixing_produce_lfe = c->remixing_consume_lfe = k; + + pa_log("[%s:%u] Deprecated option 'enable-lfe-remixing' found.", state->filename, state->lineno); + pa_log("[%s:%u] Please migrate to 'remixing-produce-lfe' and 'remixing-consume-lfe', set both to '%s'.", + state->filename, state->lineno, pa_yes_no(c->remixing_produce_lfe)); + + return 0; +} + +#ifdef HAVE_DBUS +static int parse_server_type(pa_config_parser_state *state) { + pa_daemon_conf *c; + + pa_assert(state); + + c = state->data; + + if (pa_daemon_conf_set_local_server_type(c, state->rvalue) < 0) { + pa_log(_("[%s:%u] Invalid server type '%s'."), state->filename, state->lineno, state->rvalue); + return -1; + } + + return 0; +} +#endif + +int pa_daemon_conf_load(pa_daemon_conf *c, const char *filename) { + int r = -1; + FILE *f = NULL; + struct channel_conf_info ci; + pa_config_item table[] = { + { "daemonize", pa_config_parse_bool, &c->daemonize, NULL }, + { "fail", pa_config_parse_bool, &c->fail, NULL }, + { "high-priority", pa_config_parse_bool, &c->high_priority, NULL }, + { "realtime-scheduling", pa_config_parse_bool, &c->realtime_scheduling, NULL }, + { "disallow-module-loading", pa_config_parse_bool, &c->disallow_module_loading, NULL }, + { "allow-module-loading", pa_config_parse_not_bool, &c->disallow_module_loading, NULL }, + { "disallow-exit", pa_config_parse_bool, &c->disallow_exit, NULL }, + { "allow-exit", pa_config_parse_not_bool, &c->disallow_exit, NULL }, + { "use-pid-file", pa_config_parse_bool, &c->use_pid_file, NULL }, + { "system-instance", pa_config_parse_bool, &c->system_instance, NULL }, +#ifdef HAVE_DBUS + { "local-server-type", parse_server_type, c, NULL }, +#endif + { "no-cpu-limit", pa_config_parse_bool, &c->no_cpu_limit, NULL }, + { "cpu-limit", pa_config_parse_not_bool, &c->no_cpu_limit, NULL }, + { "disable-shm", pa_config_parse_bool, &c->disable_shm, NULL }, + { "enable-shm", pa_config_parse_not_bool, &c->disable_shm, NULL }, + { "enable-memfd", pa_config_parse_not_bool, &c->disable_memfd, NULL }, + { "flat-volumes", pa_config_parse_bool, &c->flat_volumes, NULL }, + { "rescue-streams", pa_config_parse_bool, &c->rescue_streams, NULL }, + { "lock-memory", pa_config_parse_bool, &c->lock_memory, NULL }, + { "enable-deferred-volume", pa_config_parse_bool, &c->deferred_volume, NULL }, + { "exit-idle-time", pa_config_parse_int, &c->exit_idle_time, NULL }, + { "scache-idle-time", pa_config_parse_int, &c->scache_idle_time, NULL }, + { "realtime-priority", parse_rtprio, c, NULL }, + { "dl-search-path", pa_config_parse_string, &c->dl_search_path, NULL }, + { "default-script-file", pa_config_parse_string, &c->default_script_file, NULL }, + { "log-target", parse_log_target, c, NULL }, + { "log-level", parse_log_level, c, NULL }, + { "verbose", parse_log_level, c, NULL }, + { "resample-method", parse_resample_method, c, NULL }, + { "default-sample-format", parse_sample_format, c, NULL }, + { "default-sample-rate", parse_sample_rate, c, NULL }, + { "alternate-sample-rate", parse_alternate_sample_rate, c, NULL }, + { "default-sample-channels", parse_sample_channels, &ci, NULL }, + { "default-channel-map", parse_channel_map, &ci, NULL }, + { "default-fragments", parse_fragments, c, NULL }, + { "default-fragment-size-msec", parse_fragment_size_msec, c, NULL }, + { "deferred-volume-safety-margin-usec", + pa_config_parse_unsigned, &c->deferred_volume_safety_margin_usec, NULL }, + { "deferred-volume-extra-delay-usec", + pa_config_parse_int, &c->deferred_volume_extra_delay_usec, NULL }, + { "nice-level", parse_nice_level, c, NULL }, + { "avoid-resampling", pa_config_parse_bool, &c->avoid_resampling, NULL }, + { "disable-remixing", pa_config_parse_bool, &c->disable_remixing, NULL }, + { "enable-remixing", pa_config_parse_not_bool, &c->disable_remixing, NULL }, + { "remixing-use-all-sink-channels", + pa_config_parse_bool, &c->remixing_use_all_sink_channels, NULL }, + { "disable-lfe-remixing", parse_disable_lfe_remix, c, NULL }, + { "enable-lfe-remixing", parse_enable_lfe_remix, c, NULL }, + { "remixing-produce-lfe", pa_config_parse_bool, &c->remixing_produce_lfe, NULL }, + { "remixing-consume-lfe", pa_config_parse_bool, &c->remixing_consume_lfe, NULL }, + { "lfe-crossover-freq", pa_config_parse_unsigned, &c->lfe_crossover_freq, NULL }, + { "load-default-script-file", pa_config_parse_bool, &c->load_default_script_file, NULL }, + { "shm-size-bytes", pa_config_parse_size, &c->shm_size, NULL }, + { "log-meta", pa_config_parse_bool, &c->log_meta, NULL }, + { "log-time", pa_config_parse_bool, &c->log_time, NULL }, + { "log-backtrace", pa_config_parse_unsigned, &c->log_backtrace, NULL }, +#ifdef HAVE_SYS_RESOURCE_H + { "rlimit-fsize", parse_rlimit, &c->rlimit_fsize, NULL }, + { "rlimit-data", parse_rlimit, &c->rlimit_data, NULL }, + { "rlimit-stack", parse_rlimit, &c->rlimit_stack, NULL }, + { "rlimit-core", parse_rlimit, &c->rlimit_core, NULL }, +#ifdef RLIMIT_RSS + { "rlimit-rss", parse_rlimit, &c->rlimit_rss, NULL }, +#endif +#ifdef RLIMIT_NOFILE + { "rlimit-nofile", parse_rlimit, &c->rlimit_nofile, NULL }, +#endif +#ifdef RLIMIT_AS + { "rlimit-as", parse_rlimit, &c->rlimit_as, NULL }, +#endif +#ifdef RLIMIT_NPROC + { "rlimit-nproc", parse_rlimit, &c->rlimit_nproc, NULL }, +#endif +#ifdef RLIMIT_MEMLOCK + { "rlimit-memlock", parse_rlimit, &c->rlimit_memlock, NULL }, +#endif +#ifdef RLIMIT_LOCKS + { "rlimit-locks", parse_rlimit, &c->rlimit_locks, NULL }, +#endif +#ifdef RLIMIT_SIGPENDING + { "rlimit-sigpending", parse_rlimit, &c->rlimit_sigpending, NULL }, +#endif +#ifdef RLIMIT_MSGQUEUE + { "rlimit-msgqueue", parse_rlimit, &c->rlimit_msgqueue, NULL }, +#endif +#ifdef RLIMIT_NICE + { "rlimit-nice", parse_rlimit, &c->rlimit_nice, NULL }, +#endif +#ifdef RLIMIT_RTPRIO + { "rlimit-rtprio", parse_rlimit, &c->rlimit_rtprio, NULL }, +#endif +#ifdef RLIMIT_RTTIME + { "rlimit-rttime", parse_rlimit, &c->rlimit_rttime, NULL }, +#endif +#endif + { NULL, NULL, NULL, NULL }, + }; + + pa_xfree(c->config_file); + c->config_file = NULL; + + f = filename ? + pa_fopen_cloexec(c->config_file = pa_xstrdup(filename), "r") : + pa_open_config_file(DEFAULT_CONFIG_FILE, DEFAULT_CONFIG_FILE_USER, ENV_CONFIG_FILE, &c->config_file); + + if (!f && errno != ENOENT) { + pa_log_warn(_("Failed to open configuration file: %s"), pa_cstrerror(errno)); + goto finish; + } + + ci.default_channel_map_set = ci.default_sample_spec_set = false; + ci.conf = c; + + r = f ? pa_config_parse(c->config_file, f, table, NULL, true, NULL) : 0; + + if (r >= 0) { + + /* Make sure that channel map and sample spec fit together */ + + if (ci.default_sample_spec_set && + ci.default_channel_map_set && + c->default_channel_map.channels != c->default_sample_spec.channels) { + pa_log_error(_("The specified default channel map has a different number of channels than the specified default number of channels.")); + r = -1; + goto finish; + } else if (ci.default_sample_spec_set) + pa_channel_map_init_extend(&c->default_channel_map, c->default_sample_spec.channels, PA_CHANNEL_MAP_DEFAULT); + else if (ci.default_channel_map_set) + c->default_sample_spec.channels = c->default_channel_map.channels; + } + +finish: + if (f) + fclose(f); + + return r; +} + +int pa_daemon_conf_env(pa_daemon_conf *c) { + char *e; + pa_assert(c); + + if ((e = getenv(ENV_DL_SEARCH_PATH))) { + pa_xfree(c->dl_search_path); + c->dl_search_path = pa_xstrdup(e); + } + if ((e = getenv(ENV_SCRIPT_FILE))) { + pa_xfree(c->default_script_file); + c->default_script_file = pa_xstrdup(e); + } + + return 0; +} + +const char *pa_daemon_conf_get_default_script_file(pa_daemon_conf *c) { + pa_assert(c); + + if (!c->default_script_file) { + if (c->system_instance) + c->default_script_file = pa_find_config_file(DEFAULT_SYSTEM_SCRIPT_FILE, NULL, ENV_SCRIPT_FILE); + else + c->default_script_file = pa_find_config_file(DEFAULT_SCRIPT_FILE, DEFAULT_SCRIPT_FILE_USER, ENV_SCRIPT_FILE); + } + + return c->default_script_file; +} + +FILE *pa_daemon_conf_open_default_script_file(pa_daemon_conf *c) { + FILE *f; + pa_assert(c); + + if (!c->default_script_file) { + if (c->system_instance) + f = pa_open_config_file(DEFAULT_SYSTEM_SCRIPT_FILE, NULL, ENV_SCRIPT_FILE, &c->default_script_file); + else + f = pa_open_config_file(DEFAULT_SCRIPT_FILE, DEFAULT_SCRIPT_FILE_USER, ENV_SCRIPT_FILE, &c->default_script_file); + } else + f = pa_fopen_cloexec(c->default_script_file, "r"); + + return f; +} + +char *pa_daemon_conf_dump(pa_daemon_conf *c) { + static const char* const log_level_to_string[] = { + [PA_LOG_DEBUG] = "debug", + [PA_LOG_INFO] = "info", + [PA_LOG_NOTICE] = "notice", + [PA_LOG_WARN] = "warning", + [PA_LOG_ERROR] = "error" + }; + +#ifdef HAVE_DBUS + static const char* const server_type_to_string[] = { + [PA_SERVER_TYPE_UNSET] = "!!UNSET!!", + [PA_SERVER_TYPE_USER] = "user", + [PA_SERVER_TYPE_SYSTEM] = "system", + [PA_SERVER_TYPE_NONE] = "none" + }; +#endif + + pa_strbuf *s; + char cm[PA_CHANNEL_MAP_SNPRINT_MAX]; + char *log_target = NULL; + + pa_assert(c); + + s = pa_strbuf_new(); + + if (c->config_file) + pa_strbuf_printf(s, _("### Read from configuration file: %s ###\n"), c->config_file); + + pa_assert(c->log_level < PA_LOG_LEVEL_MAX); + + if (c->log_target) + log_target = pa_log_target_to_string(c->log_target); + + pa_strbuf_printf(s, "daemonize = %s\n", pa_yes_no(c->daemonize)); + pa_strbuf_printf(s, "fail = %s\n", pa_yes_no(c->fail)); + pa_strbuf_printf(s, "high-priority = %s\n", pa_yes_no(c->high_priority)); + pa_strbuf_printf(s, "nice-level = %i\n", c->nice_level); + pa_strbuf_printf(s, "realtime-scheduling = %s\n", pa_yes_no(c->realtime_scheduling)); + pa_strbuf_printf(s, "realtime-priority = %i\n", c->realtime_priority); + pa_strbuf_printf(s, "allow-module-loading = %s\n", pa_yes_no(!c->disallow_module_loading)); + pa_strbuf_printf(s, "allow-exit = %s\n", pa_yes_no(!c->disallow_exit)); + pa_strbuf_printf(s, "use-pid-file = %s\n", pa_yes_no(c->use_pid_file)); + pa_strbuf_printf(s, "system-instance = %s\n", pa_yes_no(c->system_instance)); +#ifdef HAVE_DBUS + pa_strbuf_printf(s, "local-server-type = %s\n", server_type_to_string[c->local_server_type]); +#endif + pa_strbuf_printf(s, "cpu-limit = %s\n", pa_yes_no(!c->no_cpu_limit)); + pa_strbuf_printf(s, "enable-shm = %s\n", pa_yes_no(!c->disable_shm)); + pa_strbuf_printf(s, "flat-volumes = %s\n", pa_yes_no(c->flat_volumes)); + pa_strbuf_printf(s, "rescue-streams = %s\n", pa_yes_no(c->rescue_streams)); + pa_strbuf_printf(s, "lock-memory = %s\n", pa_yes_no(c->lock_memory)); + pa_strbuf_printf(s, "exit-idle-time = %i\n", c->exit_idle_time); + pa_strbuf_printf(s, "scache-idle-time = %i\n", c->scache_idle_time); + pa_strbuf_printf(s, "dl-search-path = %s\n", pa_strempty(c->dl_search_path)); + pa_strbuf_printf(s, "default-script-file = %s\n", pa_strempty(pa_daemon_conf_get_default_script_file(c))); + pa_strbuf_printf(s, "load-default-script-file = %s\n", pa_yes_no(c->load_default_script_file)); + pa_strbuf_printf(s, "log-target = %s\n", pa_strempty(log_target)); + pa_strbuf_printf(s, "log-level = %s\n", log_level_to_string[c->log_level]); + pa_strbuf_printf(s, "resample-method = %s\n", pa_resample_method_to_string(c->resample_method)); + pa_strbuf_printf(s, "avoid-resampling = %s\n", pa_yes_no(c->avoid_resampling)); + pa_strbuf_printf(s, "enable-remixing = %s\n", pa_yes_no(!c->disable_remixing)); + pa_strbuf_printf(s, "remixing-use-all-sink-channels = %s\n", pa_yes_no(c->remixing_use_all_sink_channels)); + pa_strbuf_printf(s, "remixing-produce-lfe = %s\n", pa_yes_no(c->remixing_produce_lfe)); + pa_strbuf_printf(s, "remixing-consume-lfe = %s\n", pa_yes_no(c->remixing_consume_lfe)); + pa_strbuf_printf(s, "lfe-crossover-freq = %u\n", c->lfe_crossover_freq); + pa_strbuf_printf(s, "default-sample-format = %s\n", pa_sample_format_to_string(c->default_sample_spec.format)); + pa_strbuf_printf(s, "default-sample-rate = %u\n", c->default_sample_spec.rate); + pa_strbuf_printf(s, "alternate-sample-rate = %u\n", c->alternate_sample_rate); + pa_strbuf_printf(s, "default-sample-channels = %u\n", c->default_sample_spec.channels); + pa_strbuf_printf(s, "default-channel-map = %s\n", pa_channel_map_snprint(cm, sizeof(cm), &c->default_channel_map)); + pa_strbuf_printf(s, "default-fragments = %u\n", c->default_n_fragments); + pa_strbuf_printf(s, "default-fragment-size-msec = %u\n", c->default_fragment_size_msec); + pa_strbuf_printf(s, "enable-deferred-volume = %s\n", pa_yes_no(c->deferred_volume)); + pa_strbuf_printf(s, "deferred-volume-safety-margin-usec = %u\n", c->deferred_volume_safety_margin_usec); + pa_strbuf_printf(s, "deferred-volume-extra-delay-usec = %d\n", c->deferred_volume_extra_delay_usec); + pa_strbuf_printf(s, "shm-size-bytes = %lu\n", (unsigned long) c->shm_size); + pa_strbuf_printf(s, "log-meta = %s\n", pa_yes_no(c->log_meta)); + pa_strbuf_printf(s, "log-time = %s\n", pa_yes_no(c->log_time)); + pa_strbuf_printf(s, "log-backtrace = %u\n", c->log_backtrace); +#ifdef HAVE_SYS_RESOURCE_H + pa_strbuf_printf(s, "rlimit-fsize = %li\n", c->rlimit_fsize.is_set ? (long int) c->rlimit_fsize.value : -1); + pa_strbuf_printf(s, "rlimit-data = %li\n", c->rlimit_data.is_set ? (long int) c->rlimit_data.value : -1); + pa_strbuf_printf(s, "rlimit-stack = %li\n", c->rlimit_stack.is_set ? (long int) c->rlimit_stack.value : -1); + pa_strbuf_printf(s, "rlimit-core = %li\n", c->rlimit_core.is_set ? (long int) c->rlimit_core.value : -1); +#ifdef RLIMIT_RSS + pa_strbuf_printf(s, "rlimit-rss = %li\n", c->rlimit_rss.is_set ? (long int) c->rlimit_rss.value : -1); +#endif +#ifdef RLIMIT_AS + pa_strbuf_printf(s, "rlimit-as = %li\n", c->rlimit_as.is_set ? (long int) c->rlimit_as.value : -1); +#endif +#ifdef RLIMIT_NPROC + pa_strbuf_printf(s, "rlimit-nproc = %li\n", c->rlimit_nproc.is_set ? (long int) c->rlimit_nproc.value : -1); +#endif +#ifdef RLIMIT_NOFILE + pa_strbuf_printf(s, "rlimit-nofile = %li\n", c->rlimit_nofile.is_set ? (long int) c->rlimit_nofile.value : -1); +#endif +#ifdef RLIMIT_MEMLOCK + pa_strbuf_printf(s, "rlimit-memlock = %li\n", c->rlimit_memlock.is_set ? (long int) c->rlimit_memlock.value : -1); +#endif +#ifdef RLIMIT_LOCKS + pa_strbuf_printf(s, "rlimit-locks = %li\n", c->rlimit_locks.is_set ? (long int) c->rlimit_locks.value : -1); +#endif +#ifdef RLIMIT_SIGPENDING + pa_strbuf_printf(s, "rlimit-sigpending = %li\n", c->rlimit_sigpending.is_set ? (long int) c->rlimit_sigpending.value : -1); +#endif +#ifdef RLIMIT_MSGQUEUE + pa_strbuf_printf(s, "rlimit-msgqueue = %li\n", c->rlimit_msgqueue.is_set ? (long int) c->rlimit_msgqueue.value : -1); +#endif +#ifdef RLIMIT_NICE + pa_strbuf_printf(s, "rlimit-nice = %li\n", c->rlimit_nice.is_set ? (long int) c->rlimit_nice.value : -1); +#endif +#ifdef RLIMIT_RTPRIO + pa_strbuf_printf(s, "rlimit-rtprio = %li\n", c->rlimit_rtprio.is_set ? (long int) c->rlimit_rtprio.value : -1); +#endif +#ifdef RLIMIT_RTTIME + pa_strbuf_printf(s, "rlimit-rttime = %li\n", c->rlimit_rttime.is_set ? (long int) c->rlimit_rttime.value : -1); +#endif +#endif + + pa_xfree(log_target); + + return pa_strbuf_to_string_free(s); +} diff --git a/frameworks/innerkitsimpl/pulseaudio/src/daemon/ohos_pa_main.c b/frameworks/innerkitsimpl/pulseaudio/src/daemon/ohos_pa_main.c new file mode 100644 index 0000000000000000000000000000000000000000..34525aac6f4780fc749c4adbd96d8eca5d98ccd8 --- /dev/null +++ b/frameworks/innerkitsimpl/pulseaudio/src/daemon/ohos_pa_main.c @@ -0,0 +1,1274 @@ +/*** + This file is part of PulseAudio. + + Copyright 2004-2006 Lennart Poettering + Copyright 2006 Pierre Ossman for Cendio AB + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, see . +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_SYS_MMAN_H +#include +#endif + +#ifdef HAVE_PWD_H +#include +#endif +#ifdef HAVE_GRP_H +#include +#endif + +#ifdef HAVE_LIBWRAP +#include +#include +#endif + +#ifdef HAVE_DBUS +#include +#endif + +#ifdef HAVE_SYSTEMD_DAEMON +#include +#endif + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_DBUS +#include +#endif +#include + +#include "cmdline.h" +#include "cpulimit.h" +#include "daemon-conf.h" +#include "dumpmodules.h" +#include "caps.h" +#include "ltdl-bind-now.h" +#include "server-lookup.h" + +#ifdef __cplusplus +#if __cplusplus +extern "C" { +#endif +#endif /* End of #ifdef __cplusplus */ +#ifdef DISABLE_LIBTOOL_PRELOAD +/* FIXME: work around a libtool bug by making sure we have 2 elements. Bug has + * been reported: https://debbugs.gnu.org/cgi/bugreport.cgi?bug=29576 */ +const lt_dlsymlist lt_preloaded_symbols[] = { + { "@PROGRAM@", NULL }, + { NULL, NULL } +}; +#endif + +#ifdef HAVE_LIBWRAP +/* Only one instance of these variables */ +int allow_severity = LOG_INFO; +int deny_severity = LOG_WARNING; +#endif + +#ifdef HAVE_OSS_WRAPPER +/* padsp looks for this symbol in the running process and disables + * itself if it finds it and it is set to 7 (which is actually a bit + * mask). For details see padsp. */ +int __padsp_disabled__ = 7; +#endif + +static void signal_callback(pa_mainloop_api* m, pa_signal_event *e, int sig, void *userdata) { + pa_module *module = NULL; + + pa_log_info("Got signal %s.", pa_sig2str(sig)); + + switch (sig) { +#ifdef SIGUSR1 + case SIGUSR1: + pa_module_load(&module, userdata, "module-cli", NULL); + break; +#endif + +#ifdef SIGUSR2 + case SIGUSR2: + pa_module_load(&module, userdata, "module-cli-protocol-unix", NULL); + break; +#endif + +#ifdef SIGHUP + case SIGHUP: { + char *c = pa_full_status_string(userdata); + pa_log_notice("%s", c); + pa_xfree(c); + return; + } +#endif + + case SIGINT: + case SIGTERM: + default: + pa_log_info("Exiting."); + m->quit(m, 0); + break; + } +} + +#if defined(HAVE_PWD_H) && defined(HAVE_GRP_H) + +static int change_user(void) { + struct passwd *pw; + struct group * gr; + int r; + + /* This function is called only in system-wide mode. It creates a + * runtime dir in /var/run/ with proper UID/GID and drops privs + * afterwards. */ + + if (!(pw = getpwnam(PA_SYSTEM_USER))) { + pa_log(_("Failed to find user '%s'."), PA_SYSTEM_USER); + return -1; + } + + if (!(gr = getgrnam(PA_SYSTEM_GROUP))) { + pa_log(_("Failed to find group '%s'."), PA_SYSTEM_GROUP); + return -1; + } + + pa_log_info("Found user '%s' (UID %lu) and group '%s' (GID %lu).", + PA_SYSTEM_USER, (unsigned long) pw->pw_uid, + PA_SYSTEM_GROUP, (unsigned long) gr->gr_gid); + + if (pw->pw_gid != gr->gr_gid) { + pa_log(_("GID of user '%s' and of group '%s' don't match."), PA_SYSTEM_USER, PA_SYSTEM_GROUP); + return -1; + } + + if (!pa_streq(pw->pw_dir, PA_SYSTEM_RUNTIME_PATH)) + pa_log_warn(_("Home directory of user '%s' is not '%s', ignoring."), PA_SYSTEM_USER, PA_SYSTEM_RUNTIME_PATH); + + if (pa_make_secure_dir(PA_SYSTEM_RUNTIME_PATH, 0755, pw->pw_uid, gr->gr_gid, true) < 0) { + pa_log(_("Failed to create '%s': %s"), PA_SYSTEM_RUNTIME_PATH, pa_cstrerror(errno)); + return -1; + } + + if (pa_make_secure_dir(PA_SYSTEM_STATE_PATH, 0700, pw->pw_uid, gr->gr_gid, true) < 0) { + pa_log(_("Failed to create '%s': %s"), PA_SYSTEM_STATE_PATH, pa_cstrerror(errno)); + return -1; + } + + /* We don't create the config dir here, because we don't need to write to it */ + + if (initgroups(PA_SYSTEM_USER, gr->gr_gid) != 0) { + pa_log(_("Failed to change group list: %s"), pa_cstrerror(errno)); + return -1; + } + +#if defined(HAVE_SETRESGID) + r = setresgid(gr->gr_gid, gr->gr_gid, gr->gr_gid); +#elif defined(HAVE_SETEGID) + if ((r = setgid(gr->gr_gid)) >= 0) + r = setegid(gr->gr_gid); +#elif defined(HAVE_SETREGID) + r = setregid(gr->gr_gid, gr->gr_gid); +#else +#error "No API to drop privileges" +#endif + + if (r < 0) { + pa_log(_("Failed to change GID: %s"), pa_cstrerror(errno)); + return -1; + } + +#if defined(HAVE_SETRESUID) + r = setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid); +#elif defined(HAVE_SETEUID) + if ((r = setuid(pw->pw_uid)) >= 0) + r = seteuid(pw->pw_uid); +#elif defined(HAVE_SETREUID) + r = setreuid(pw->pw_uid, pw->pw_uid); +#else +#error "No API to drop privileges" +#endif + + if (r < 0) { + pa_log(_("Failed to change UID: %s"), pa_cstrerror(errno)); + return -1; + } + + pa_drop_caps(); + + pa_set_env("USER", PA_SYSTEM_USER); + pa_set_env("USERNAME", PA_SYSTEM_USER); + pa_set_env("LOGNAME", PA_SYSTEM_USER); + pa_set_env("HOME", PA_SYSTEM_RUNTIME_PATH); + + /* Relevant for pa_runtime_path() */ + if (!getenv("PULSE_RUNTIME_PATH")) + pa_set_env("PULSE_RUNTIME_PATH", PA_SYSTEM_RUNTIME_PATH); + + if (!getenv("PULSE_CONFIG_PATH")) + pa_set_env("PULSE_CONFIG_PATH", PA_SYSTEM_CONFIG_PATH); + + if (!getenv("PULSE_STATE_PATH")) + pa_set_env("PULSE_STATE_PATH", PA_SYSTEM_STATE_PATH); + + pa_log_info("Successfully changed user to \"" PA_SYSTEM_USER "\"."); + + return 0; +} + +#else /* HAVE_PWD_H && HAVE_GRP_H */ + +static int change_user(void) { + pa_log(_("System wide mode unsupported on this platform.")); + return -1; +} + +#endif /* HAVE_PWD_H && HAVE_GRP_H */ + +#ifdef HAVE_SYS_RESOURCE_H + +static int set_one_rlimit(const pa_rlimit *r, int resource, const char *name) { + struct rlimit rl; + pa_assert(r); + + if (!r->is_set) + return 0; + + rl.rlim_cur = rl.rlim_max = r->value; + + if (setrlimit(resource, &rl) < 0) { + pa_log_info("setrlimit(%s, (%u, %u)) failed: %s", name, (unsigned) r->value, (unsigned) r->value, pa_cstrerror(errno)); + return -1; + } + + return 0; +} + +static void set_all_rlimits(const pa_daemon_conf *conf) { + set_one_rlimit(&conf->rlimit_fsize, RLIMIT_FSIZE, "RLIMIT_FSIZE"); + set_one_rlimit(&conf->rlimit_data, RLIMIT_DATA, "RLIMIT_DATA"); + set_one_rlimit(&conf->rlimit_stack, RLIMIT_STACK, "RLIMIT_STACK"); + set_one_rlimit(&conf->rlimit_core, RLIMIT_CORE, "RLIMIT_CORE"); +#ifdef RLIMIT_RSS + set_one_rlimit(&conf->rlimit_rss, RLIMIT_RSS, "RLIMIT_RSS"); +#endif +#ifdef RLIMIT_NPROC + set_one_rlimit(&conf->rlimit_nproc, RLIMIT_NPROC, "RLIMIT_NPROC"); +#endif +#ifdef RLIMIT_NOFILE + set_one_rlimit(&conf->rlimit_nofile, RLIMIT_NOFILE, "RLIMIT_NOFILE"); +#endif +#ifdef RLIMIT_MEMLOCK + set_one_rlimit(&conf->rlimit_memlock, RLIMIT_MEMLOCK, "RLIMIT_MEMLOCK"); +#endif +#ifdef RLIMIT_AS + set_one_rlimit(&conf->rlimit_as, RLIMIT_AS, "RLIMIT_AS"); +#endif +#ifdef RLIMIT_LOCKS + set_one_rlimit(&conf->rlimit_locks, RLIMIT_LOCKS, "RLIMIT_LOCKS"); +#endif +#ifdef RLIMIT_SIGPENDING + set_one_rlimit(&conf->rlimit_sigpending, RLIMIT_SIGPENDING, "RLIMIT_SIGPENDING"); +#endif +#ifdef RLIMIT_MSGQUEUE + set_one_rlimit(&conf->rlimit_msgqueue, RLIMIT_MSGQUEUE, "RLIMIT_MSGQUEUE"); +#endif +#ifdef RLIMIT_NICE + set_one_rlimit(&conf->rlimit_nice, RLIMIT_NICE, "RLIMIT_NICE"); +#endif +#ifdef RLIMIT_RTPRIO + set_one_rlimit(&conf->rlimit_rtprio, RLIMIT_RTPRIO, "RLIMIT_RTPRIO"); +#endif +#ifdef RLIMIT_RTTIME + set_one_rlimit(&conf->rlimit_rttime, RLIMIT_RTTIME, "RLIMIT_RTTIME"); +#endif +} +#endif + +static char *check_configured_address(void) { + char *default_server = NULL; + pa_client_conf *c = pa_client_conf_new(); + + pa_client_conf_load(c, true, true); + + if (c->default_server && *c->default_server) + default_server = pa_xstrdup(c->default_server); + + pa_client_conf_free(c); + + return default_server; +} + +#ifdef HAVE_DBUS +static pa_dbus_connection *register_dbus_name(pa_core *c, DBusBusType bus, const char* name) { + DBusError error; + pa_dbus_connection *conn; + + dbus_error_init(&error); + + if (!(conn = pa_dbus_bus_get(c, bus, &error)) || dbus_error_is_set(&error)) { + pa_log_warn("Unable to contact D-Bus: %s: %s", error.name, error.message); + goto fail; + } + + if (dbus_bus_request_name(pa_dbus_connection_get(conn), name, DBUS_NAME_FLAG_DO_NOT_QUEUE, &error) == DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) { + pa_log_debug("Got %s!", name); + return conn; + } + + if (dbus_error_is_set(&error)) + pa_log_error("Failed to acquire %s: %s: %s", name, error.name, error.message); + else + pa_log_error("D-Bus name %s already taken.", name); + + /* PA cannot be started twice by the same user and hence we can + * ignore mostly the case that a name is already taken. */ + +fail: + if (conn) + pa_dbus_connection_unref(conn); + + dbus_error_free(&error); + return NULL; +} +#endif + +int ohos_pa_main(int argc, char *argv[]) { + pa_core *c = NULL; + pa_strbuf *buf = NULL; + pa_daemon_conf *conf = NULL; + pa_mainloop *mainloop = NULL; + char *s; + char *configured_address; + int r = 0, retval = 1, d = 0; + bool valid_pid_file = false; +#ifdef HAVE_NO_OHOS + bool ltdl_init = false; +#endif + int n_fds = 0, *passed_fds = NULL; + const char *e; +#ifdef HAVE_FORK + int daemon_pipe[2] = { -1, -1 }; + int daemon_pipe2[2] = { -1, -1 }; +#endif + int autospawn_fd = -1; + bool autospawn_locked = false; +#ifdef HAVE_DBUS + pa_dbusobj_server_lookup *server_lookup = NULL; /* /org/pulseaudio/server_lookup */ + pa_dbus_connection *lookup_service_bus = NULL; /* Always the user bus. */ + pa_dbus_connection *server_bus = NULL; /* The bus where we reserve org.pulseaudio.Server, either the user or the system bus. */ + bool start_server; +#endif + + pa_log_set_ident("pulseaudio"); + pa_log_set_level(PA_LOG_NOTICE); + pa_log_set_flags(PA_LOG_COLORS|PA_LOG_PRINT_FILE|PA_LOG_PRINT_LEVEL, PA_LOG_RESET); + +#if !defined(HAVE_BIND_NOW) && defined(__linux__) && defined(__OPTIMIZE__) + /* + Disable lazy relocations to make usage of external libraries + more deterministic for our RT threads. We abuse __OPTIMIZE__ as + a check whether we are a debug build or not. This all is + admittedly a bit snake-oilish. + */ + + if (!getenv("LD_BIND_NOW")) { + char *rp; + char *canonical_rp; + + /* We have to execute ourselves, because the libc caches the + * value of $LD_BIND_NOW on initialization. */ + + pa_set_env("LD_BIND_NOW", "1"); + + if ((canonical_rp = pa_realpath(PA_BINARY))) { + + if ((rp = pa_readlink("/proc/self/exe"))) { + + if (pa_streq(rp, canonical_rp)) + pa_assert_se(execv(rp, argv) == 0); + else + pa_log_warn("/proc/self/exe does not point to %s, cannot self execute. Are you playing games?", canonical_rp); + + pa_xfree(rp); + + } else + pa_log_warn("Couldn't read /proc/self/exe, cannot self execute. Running in a chroot()?"); + + pa_xfree(canonical_rp); + + } else + pa_log_warn("Couldn't canonicalize binary path, cannot self execute."); + } +#endif + +#ifdef HAVE_SYSTEMD_DAEMON + n_fds = sd_listen_fds(0); + if (n_fds > 0) { + int i = n_fds; + + passed_fds = pa_xnew(int, n_fds+2); + passed_fds[n_fds] = passed_fds[n_fds+1] = -1; + while (i--) + passed_fds[i] = SD_LISTEN_FDS_START + i; + } +#endif + + if (!passed_fds) { + n_fds = 0; + passed_fds = pa_xnew(int, 2); + passed_fds[0] = passed_fds[1] = -1; + } + + if ((e = getenv("PULSE_PASSED_FD"))) { + int passed_fd = atoi(e); + if (passed_fd > 2) + passed_fds[n_fds] = passed_fd; + } + + /* We might be autospawned, in which case have no idea in which + * context we have been started. Let's cleanup our execution + * context as good as possible */ + + pa_reset_personality(); + pa_drop_root(); +#ifdef HAVE_NO_OHOS + pa_close_allv(passed_fds); + pa_xfree(passed_fds); +#endif + pa_reset_sigs(-1); + pa_unblock_sigs(-1); + pa_reset_priority(); + + /* Load locale from the environment. */ + setlocale(LC_ALL, ""); + + /* Set LC_NUMERIC to C so that floating point strings are consistently + * formatted and parsed across locales. */ + setlocale(LC_NUMERIC, "C"); + + pa_init_i18n(); + + conf = pa_daemon_conf_new(); + + if (pa_daemon_conf_load(conf, NULL) < 0) + goto finish; + + if (pa_daemon_conf_env(conf) < 0) + goto finish; + + if (pa_cmdline_parse(conf, argc, argv, &d) < 0) { + pa_log(_("Failed to parse command line.")); + goto finish; + } + + if (conf->log_target) + pa_log_set_target(conf->log_target); + else { + pa_log_target target = { .type = PA_LOG_STDERR, .file = NULL }; + pa_log_set_target(&target); + } + + pa_log_set_level(conf->log_level); + if (conf->log_meta) + pa_log_set_flags(PA_LOG_PRINT_META, PA_LOG_SET); + if (conf->log_time) + pa_log_set_flags(PA_LOG_PRINT_TIME, PA_LOG_SET); + pa_log_set_show_backtrace(conf->log_backtrace); + +#ifdef HAVE_DBUS + /* conf->system_instance and conf->local_server_type control almost the + * same thing; make them agree about what is requested. */ + switch (conf->local_server_type) { + case PA_SERVER_TYPE_UNSET: + conf->local_server_type = conf->system_instance ? PA_SERVER_TYPE_SYSTEM : PA_SERVER_TYPE_USER; + break; + case PA_SERVER_TYPE_USER: + case PA_SERVER_TYPE_NONE: + conf->system_instance = false; + break; + case PA_SERVER_TYPE_SYSTEM: + conf->system_instance = true; + break; + default: + pa_assert_not_reached(); + } + + start_server = conf->local_server_type == PA_SERVER_TYPE_USER || (getuid() == 0 && conf->local_server_type == PA_SERVER_TYPE_SYSTEM); + + if (!start_server && conf->local_server_type == PA_SERVER_TYPE_SYSTEM) { + pa_log_notice(_("System mode refused for non-root user. Only starting the D-Bus server lookup service.")); + conf->system_instance = false; + } +#endif + +#ifdef HAVE_NO_OHOS + LTDL_SET_PRELOADED_SYMBOLS(); + pa_ltdl_init(); + ltdl_init = true; + + if (conf->dl_search_path) + lt_dlsetsearchpath(conf->dl_search_path); +#endif + +#ifdef OS_IS_WIN32 + { + WSADATA data; + WSAStartup(MAKEWORD(2, 0), &data); + } +#endif + + pa_random_seed(); + + switch (conf->cmd) { + case PA_CMD_DUMP_MODULES: +#ifdef HAVE_NO_OHOS + pa_dump_modules(conf, argc-d, argv+d); +#endif + retval = 0; + goto finish; + + case PA_CMD_DUMP_CONF: { + + if (d < argc) { + pa_log("Too many arguments."); + goto finish; + } + + s = pa_daemon_conf_dump(conf); + fputs(s, stdout); + pa_xfree(s); + retval = 0; + goto finish; + } + + case PA_CMD_DUMP_RESAMPLE_METHODS: { + int i; + + if (d < argc) { + pa_log("Too many arguments."); + goto finish; + } + + for (i = 0; i < PA_RESAMPLER_MAX; i++) + if (pa_resample_method_supported(i)) + printf("%s\n", pa_resample_method_to_string(i)); + + retval = 0; + goto finish; + } + + case PA_CMD_HELP : + pa_cmdline_help(argv[0]); + retval = 0; + goto finish; + + case PA_CMD_VERSION : + + if (d < argc) { + pa_log("Too many arguments."); + goto finish; + } + + printf(PACKAGE_NAME" "PACKAGE_VERSION"\n"); + retval = 0; + goto finish; + + case PA_CMD_CHECK: { + pid_t pid; + + if (d < argc) { + pa_log("Too many arguments."); + goto finish; + } + + if (pa_pid_file_check_running(&pid, "pulseaudio") < 0) + pa_log_info("Daemon not running"); + else { + pa_log_info("Daemon running as PID %u", pid); + retval = 0; + } + + goto finish; + + } + case PA_CMD_KILL: + + if (d < argc) { + pa_log("Too many arguments."); + goto finish; + } + + if (pa_pid_file_kill(SIGINT, NULL, "pulseaudio") < 0) + pa_log(_("Failed to kill daemon: %s"), pa_cstrerror(errno)); + else + retval = 0; + + goto finish; + + case PA_CMD_CLEANUP_SHM: + + if (d < argc) { + pa_log("Too many arguments."); + goto finish; + } + + if (pa_shm_cleanup() >= 0) + retval = 0; + + goto finish; + + default: + pa_assert(conf->cmd == PA_CMD_DAEMON || conf->cmd == PA_CMD_START); + } + + if (d < argc) { + pa_log("Too many arguments."); + goto finish; + } + +#ifdef HAVE_GETUID + if (getuid() == 0 && !conf->system_instance) + pa_log_warn(_("This program is not intended to be run as root (unless --system is specified).")); +#ifndef HAVE_DBUS /* A similar, only a notice worthy check was done earlier, if D-Bus is enabled. */ + else if (getuid() != 0 && conf->system_instance) { + pa_log(_("Root privileges required.")); + goto finish; + } +#endif +#endif /* HAVE_GETUID */ + + if (conf->cmd == PA_CMD_START && conf->system_instance) { + pa_log(_("--start not supported for system instances.")); + goto finish; + } + + if (conf->cmd == PA_CMD_START && (configured_address = check_configured_address())) { + /* There is an server address in our config, but where did it come from? + * By default a standard X11 login will load module-x11-publish which will + * inject PULSE_SERVER X11 property. If the PA daemon crashes, we will end + * up hitting this code path. So we have to check to see if our configured_address + * is the same as the value that would go into this property so that we can + * recover (i.e. autospawn) from a crash. + */ + char *ufn; + bool start_anyway = false; + + if ((ufn = pa_runtime_path(PA_NATIVE_DEFAULT_UNIX_SOCKET))) { + char *id; + + if ((id = pa_machine_id())) { + pa_strlist *server_list; + char formatted_ufn[256]; + + pa_snprintf(formatted_ufn, sizeof(formatted_ufn), "{%s}unix:%s", id, ufn); + pa_xfree(id); + + if ((server_list = pa_strlist_parse(configured_address))) { + char *u = NULL; + + /* We only need to check the first server */ + server_list = pa_strlist_pop(server_list, &u); + pa_strlist_free(server_list); + + start_anyway = (u && pa_streq(formatted_ufn, u)); + pa_xfree(u); + } + } + pa_xfree(ufn); + } + + if (!start_anyway) { + pa_log_notice(_("User-configured server at %s, refusing to start/autospawn."), configured_address); + pa_xfree(configured_address); + retval = 0; + goto finish; + } + + pa_log_notice(_("User-configured server at %s, which appears to be local. Probing deeper."), configured_address); + pa_xfree(configured_address); + } + + if (conf->system_instance && !conf->disallow_exit) + pa_log_warn(_("Running in system mode, but --disallow-exit not set.")); + + if (conf->system_instance && !conf->disallow_module_loading) + pa_log_warn(_("Running in system mode, but --disallow-module-loading not set.")); + + if (conf->system_instance && !conf->disable_shm) { + pa_log_notice(_("Running in system mode, forcibly disabling SHM mode.")); + conf->disable_shm = true; + } + + if (conf->system_instance && conf->exit_idle_time >= 0) { + pa_log_notice(_("Running in system mode, forcibly disabling exit idle time.")); + conf->exit_idle_time = -1; + } + + if (conf->cmd == PA_CMD_START) { + /* If we shall start PA only when it is not running yet, we + * first take the autospawn lock to make things + * synchronous. */ + + /* This locking and thread synchronisation code doesn't work reliably + * on kFreeBSD (Debian bug #705435), or in upstream FreeBSD ports + * (bug reference: ports/128947, patched in SVN r231972). */ +#if !defined(__FreeBSD__) && !defined(__FreeBSD_kernel__) + if ((autospawn_fd = pa_autospawn_lock_init()) < 0) { + pa_log("Failed to initialize autospawn lock"); + goto finish; + } + + if ((pa_autospawn_lock_acquire(true) < 0)) { + pa_log("Failed to acquire autospawn lock"); + goto finish; + } + + autospawn_locked = true; +#endif + } + + if (conf->daemonize) { +#ifdef HAVE_FORK + pid_t child; +#endif + + if (pa_stdio_acquire() < 0) { + pa_log(_("Failed to acquire stdio.")); + goto finish; + } + +#ifdef HAVE_FORK + if (pipe(daemon_pipe) < 0) { + pa_log(_("pipe() failed: %s"), pa_cstrerror(errno)); + goto finish; + } + + if ((child = fork()) < 0) { + pa_log(_("fork() failed: %s"), pa_cstrerror(errno)); + pa_close_pipe(daemon_pipe); + goto finish; + } + + if (child != 0) { + ssize_t n; + /* Father */ + + pa_assert_se(pa_close(daemon_pipe[1]) == 0); + daemon_pipe[1] = -1; + + if ((n = pa_loop_read(daemon_pipe[0], &retval, sizeof(retval), NULL)) != sizeof(retval)) { + + if (n < 0) + pa_log(_("read() failed: %s"), pa_cstrerror(errno)); + + retval = 1; + } + + if (retval) + pa_log(_("Daemon startup failed.")); + else + pa_log_info("Daemon startup successful."); + + goto finish; + } + + if (autospawn_fd >= 0) { + /* The lock file is unlocked from the parent, so we need + * to close it in the child */ + + pa_autospawn_lock_release(); + pa_autospawn_lock_done(true); + + autospawn_locked = false; + autospawn_fd = -1; + } + + pa_assert_se(pa_close(daemon_pipe[0]) == 0); + daemon_pipe[0] = -1; +#endif + + if (!conf->log_target) { +#ifdef HAVE_SYSTEMD_JOURNAL + pa_log_target target = { .type = PA_LOG_JOURNAL, .file = NULL }; +#else + pa_log_target target = { .type = PA_LOG_SYSLOG, .file = NULL }; +#endif + pa_log_set_target(&target); + } + +#ifdef HAVE_SETSID + if (setsid() < 0) { + pa_log(_("setsid() failed: %s"), pa_cstrerror(errno)); + goto finish; + } +#endif + +#ifdef HAVE_FORK + /* We now are a session and process group leader. Let's fork + * again and let the father die, so that we'll become a + * process that can never acquire a TTY again, in a session and + * process group without leader */ + + if (pipe(daemon_pipe2) < 0) { + pa_log(_("pipe() failed: %s"), pa_cstrerror(errno)); + goto finish; + } + + if ((child = fork()) < 0) { + pa_log(_("fork() failed: %s"), pa_cstrerror(errno)); + pa_close_pipe(daemon_pipe2); + goto finish; + } + + if (child != 0) { + ssize_t n; + /* Father */ + + pa_assert_se(pa_close(daemon_pipe2[1]) == 0); + daemon_pipe2[1] = -1; + + if ((n = pa_loop_read(daemon_pipe2[0], &retval, sizeof(retval), NULL)) != sizeof(retval)) { + + if (n < 0) + pa_log(_("read() failed: %s"), pa_cstrerror(errno)); + + retval = 1; + } + + /* We now have to take care of signalling the first fork with + * the return value we've received from this fork... */ + pa_assert(daemon_pipe[1] >= 0); + + pa_loop_write(daemon_pipe[1], &retval, sizeof(retval), NULL); + pa_close(daemon_pipe[1]); + daemon_pipe[1] = -1; + + goto finish; + } + + pa_assert_se(pa_close(daemon_pipe2[0]) == 0); + daemon_pipe2[0] = -1; + + /* We no longer need the (first) daemon_pipe as it's handled in our child above */ + pa_close_pipe(daemon_pipe); +#endif + +#ifdef SIGTTOU + signal(SIGTTOU, SIG_IGN); +#endif +#ifdef SIGTTIN + signal(SIGTTIN, SIG_IGN); +#endif +#ifdef SIGTSTP + signal(SIGTSTP, SIG_IGN); +#endif + + pa_nullify_stdfds(); + } + + pa_set_env_and_record("PULSE_INTERNAL", "1"); + pa_assert_se(chdir("/") == 0); + umask(0077); + +#ifdef HAVE_SYS_RESOURCE_H + set_all_rlimits(conf); +#endif + pa_rtclock_hrtimer_enable(); + + if (conf->high_priority) + pa_raise_priority(conf->nice_level); + + if (conf->system_instance) + if (change_user() < 0) + goto finish; + + pa_set_env_and_record("PULSE_SYSTEM", conf->system_instance ? "1" : "0"); + + pa_log_info("This is PulseAudio %s", PACKAGE_VERSION); + pa_log_debug("Compilation CFLAGS: %s", PA_CFLAGS); + +#ifdef HAVE_LIBSAMPLERATE + pa_log_warn("Compiled with DEPRECATED libsamplerate support!"); +#endif + + s = pa_uname_string(); + pa_log_debug("Running on host: %s", s); + pa_xfree(s); + + pa_log_debug("Found %u CPUs.", pa_ncpus()); + + pa_log_info("Page size is %zu bytes", pa_page_size()); + +#ifdef HAVE_VALGRIND_MEMCHECK_H + pa_log_debug("Compiled with Valgrind support: yes"); +#else + pa_log_debug("Compiled with Valgrind support: no"); +#endif + + pa_log_debug("Running in valgrind mode: %s", pa_yes_no(pa_in_valgrind())); + + pa_log_debug("Running in VM: %s", pa_yes_no(pa_running_in_vm())); + +#ifdef HAVE_RUNNING_FROM_BUILD_TREE + pa_log_debug("Running from build tree: %s", pa_yes_no(pa_run_from_build_tree())); +#else + pa_log_debug("Running from build tree: no"); +#endif + +#ifdef __OPTIMIZE__ + pa_log_debug("Optimized build: yes"); +#else + pa_log_debug("Optimized build: no"); +#endif + +#ifdef NDEBUG + pa_log_debug("NDEBUG defined, all asserts disabled."); +#elif defined(FASTPATH) + pa_log_debug("FASTPATH defined, only fast path asserts disabled."); +#else + pa_log_debug("All asserts enabled."); +#endif + + if (!(s = pa_machine_id())) { + pa_log(_("Failed to get machine ID")); + goto finish; + } + pa_log_info("Machine ID is %s.", s); + pa_xfree(s); + + if ((s = pa_session_id())) { + pa_log_info("Session ID is %s.", s); + pa_xfree(s); + } + + if (!(s = pa_get_runtime_dir())) + goto finish; + pa_log_info("Using runtime directory %s.", s); + pa_xfree(s); + + if (!(s = pa_get_state_dir())) + goto finish; + pa_log_info("Using state directory %s.", s); + pa_xfree(s); + + pa_log_info("Using modules directory %s.", conf->dl_search_path); + + pa_log_info("Running in system mode: %s", pa_yes_no(pa_in_system_mode())); + + if (pa_in_system_mode()) + pa_log_warn(_("OK, so you are running PA in system mode. Please make sure that you actually do want to do that.\n" + "Please read http://www.freedesktop.org/wiki/Software/PulseAudio/Documentation/User/WhatIsWrongWithSystemWide/ for an explanation why system mode is usually a bad idea.")); + + if (conf->use_pid_file) { + int z; + pa_pid_file_remove(); + if ((z = pa_pid_file_create("pulseaudio")) != 0) { + + if (conf->cmd == PA_CMD_START && z > 0) { + /* If we are already running and with are run in + * --start mode, then let's return this as success. */ + + retval = 0; + goto finish; + } + + pa_log(_("pa_pid_file_create() failed.")); + goto finish; + } + + valid_pid_file = true; + } + + pa_disable_sigpipe(); + + if (pa_rtclock_hrtimer()) + pa_log_info("System supports high resolution timers"); + else + pa_log_info("System appears to not support high resolution timers"); + + if (conf->lock_memory) { +#if defined(HAVE_SYS_MMAN_H) && !defined(__ANDROID__) + if (mlockall(MCL_FUTURE) < 0) + pa_log_warn("mlockall() failed: %s", pa_cstrerror(errno)); + else + pa_log_info("Successfully locked process into memory."); +#else + pa_log_warn("Memory locking requested but not supported on platform."); +#endif + } + + pa_memtrap_install(); + + pa_assert_se(mainloop = pa_mainloop_new()); + + if (!(c = pa_core_new(pa_mainloop_get_api(mainloop), !conf->disable_shm, + !conf->disable_shm && !conf->disable_memfd && pa_memfd_is_locally_supported(), + conf->shm_size))) { + pa_log(_("pa_core_new() failed.")); + goto finish; + } + + c->default_sample_spec = conf->default_sample_spec; + c->alternate_sample_rate = conf->alternate_sample_rate; + c->default_channel_map = conf->default_channel_map; + c->default_n_fragments = conf->default_n_fragments; + c->default_fragment_size_msec = conf->default_fragment_size_msec; + c->deferred_volume_safety_margin_usec = conf->deferred_volume_safety_margin_usec; + c->deferred_volume_extra_delay_usec = conf->deferred_volume_extra_delay_usec; + c->lfe_crossover_freq = conf->lfe_crossover_freq; + c->exit_idle_time = conf->exit_idle_time; + c->scache_idle_time = conf->scache_idle_time; + c->resample_method = conf->resample_method; + c->realtime_priority = conf->realtime_priority; + c->realtime_scheduling = conf->realtime_scheduling; + c->avoid_resampling = conf->avoid_resampling; + c->disable_remixing = conf->disable_remixing; + c->remixing_use_all_sink_channels = conf->remixing_use_all_sink_channels; + c->remixing_produce_lfe = conf->remixing_produce_lfe; + c->remixing_consume_lfe = conf->remixing_consume_lfe; + c->deferred_volume = conf->deferred_volume; + c->running_as_daemon = conf->daemonize; + c->disallow_exit = conf->disallow_exit; + c->flat_volumes = conf->flat_volumes; + c->rescue_streams = conf->rescue_streams; +#ifdef HAVE_DBUS + c->server_type = conf->local_server_type; +#endif + + pa_core_check_idle(c); + + c->state = PA_CORE_RUNNING; +#ifdef HAVE_CPU_INFO + pa_cpu_init(&c->cpu_info); +#endif + pa_assert_se(pa_signal_init(pa_mainloop_get_api(mainloop)) == 0); + pa_signal_new(SIGINT, signal_callback, c); + pa_signal_new(SIGTERM, signal_callback, c); +#ifdef SIGUSR1 + pa_signal_new(SIGUSR1, signal_callback, c); +#endif +#ifdef SIGUSR2 + pa_signal_new(SIGUSR2, signal_callback, c); +#endif +#ifdef SIGHUP + pa_signal_new(SIGHUP, signal_callback, c); +#endif + + if (!conf->no_cpu_limit) + pa_assert_se(pa_cpu_limit_init(pa_mainloop_get_api(mainloop)) == 0); + + buf = pa_strbuf_new(); + +#ifdef HAVE_DBUS + pa_assert_se(dbus_threads_init_default()); + + if (start_server) +#endif + { + const char *command_source = NULL; + + if (conf->load_default_script_file) { + FILE *f; + + if ((f = pa_daemon_conf_open_default_script_file(conf))) { + r = pa_cli_command_execute_file_stream(c, f, buf, &conf->fail); + fclose(f); + command_source = pa_daemon_conf_get_default_script_file(conf); + } + } + + if (r >= 0) { + r = pa_cli_command_execute(c, conf->script_commands, buf, &conf->fail); + command_source = _("command line arguments"); + } + + pa_log_error("%s", s = pa_strbuf_to_string_free(buf)); + pa_xfree(s); + + if (r < 0 && conf->fail) { + pa_log(_("Failed to initialize daemon due to errors while executing startup commands. Source of commands: %s"), command_source); + goto finish; + } + + if (!c->modules || pa_idxset_size(c->modules) == 0) { + pa_log(_("Daemon startup without any loaded modules, refusing to work.")); + goto finish; + } +#ifdef HAVE_DBUS + } else { + /* When we just provide the D-Bus server lookup service, we don't want + * any modules to be loaded. We haven't loaded any so far, so one might + * think there's no way to contact the server, but receiving certain + * signals could still cause modules to load. */ + conf->disallow_module_loading = true; +#endif + } + + /* We completed the initial module loading, so let's disable it + * from now on, if requested */ + c->disallow_module_loading = conf->disallow_module_loading; + +#ifdef HAVE_DBUS + if (!conf->system_instance) { + if ((server_lookup = pa_dbusobj_server_lookup_new(c))) { + if (!(lookup_service_bus = register_dbus_name(c, DBUS_BUS_SESSION, "org.PulseAudio1"))) + goto finish; + } + } + + if (start_server) + server_bus = register_dbus_name(c, conf->system_instance ? DBUS_BUS_SYSTEM : DBUS_BUS_SESSION, "org.pulseaudio.Server"); +#endif + +#ifdef HAVE_FORK + if (daemon_pipe2[1] >= 0) { + int ok = 0; + pa_loop_write(daemon_pipe2[1], &ok, sizeof(ok), NULL); + pa_close(daemon_pipe2[1]); + daemon_pipe2[1] = -1; + } +#endif + + pa_log_info("Daemon startup complete."); + +#ifdef HAVE_SYSTEMD_DAEMON + sd_notify(0, "READY=1"); +#endif + + retval = 0; + if (pa_mainloop_run(mainloop, &retval) < 0) + goto finish; + + pa_log_info("Daemon shutdown initiated."); + +#ifdef HAVE_SYSTEMD_DAEMON + sd_notify(0, "STOPPING=1"); +#endif + +finish: +#ifdef HAVE_DBUS + if (server_bus) + pa_dbus_connection_unref(server_bus); + if (lookup_service_bus) + pa_dbus_connection_unref(lookup_service_bus); + if (server_lookup) + pa_dbusobj_server_lookup_free(server_lookup); +#endif + + if (autospawn_fd >= 0) { + if (autospawn_locked) + pa_autospawn_lock_release(); + + pa_autospawn_lock_done(false); + } + + if (c) { + /* Ensure all the modules/samples are unloaded when the core is still ref'ed, + * as unlink callback hooks in modules may need the core to be ref'ed */ + pa_module_unload_all(c); + pa_scache_free_all(c); + + pa_core_unref(c); + pa_log_info("Daemon terminated."); + } + + if (!conf->no_cpu_limit) + pa_cpu_limit_done(); + + pa_signal_done(); + +#ifdef HAVE_FORK + /* If we have daemon_pipe[1] still open, this means we've failed after + * the first fork, but before the second. Therefore just write to it. */ + if (daemon_pipe[1] >= 0) + pa_loop_write(daemon_pipe[1], &retval, sizeof(retval), NULL); + else if (daemon_pipe2[1] >= 0) + pa_loop_write(daemon_pipe2[1], &retval, sizeof(retval), NULL); + + pa_close_pipe(daemon_pipe2); + pa_close_pipe(daemon_pipe); +#endif + + if (mainloop) + pa_mainloop_free(mainloop); + + if (conf) + pa_daemon_conf_free(conf); + + if (valid_pid_file) + pa_pid_file_remove(); + + /* This has no real purpose except making things valgrind-clean */ + pa_unset_env_recorded(); + +#ifdef OS_IS_WIN32 + WSACleanup(); +#endif + +#ifdef HAVE_NO_OHOS + if (ltdl_init) + pa_ltdl_done(); +#endif + +#ifdef HAVE_DBUS + dbus_shutdown(); +#endif + + return retval; +} + +#ifdef __cplusplus +#if __cplusplus +} +#endif +#endif /* End of #ifdef __cplusplus */ diff --git a/frameworks/innerkitsimpl/pulseaudio/src/modules/BUILD.gn b/frameworks/innerkitsimpl/pulseaudio/src/modules/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..f85914bd381bbb4c2e1a609ca8dc3f086ed9cd0c --- /dev/null +++ b/frameworks/innerkitsimpl/pulseaudio/src/modules/BUILD.gn @@ -0,0 +1,182 @@ +# Copyright (C) 2021 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") + +pulseaudio_dir = "//third_party/pulseaudio" +pulseaudio_build_path = "//foundation/multimedia/audio_standard/frameworks/innerkitsimpl/pulseaudio" + +group("modules") { + deps = [ + ":module-native-protocol-fd", + ":module-native-protocol-tcp", + ":module-native-protocol-unix", + ":module-cli-protocol-unix", + ":module-pipe-sink", + ":module-pipe-source", + ":module-suspend-on-idle" + ] +} + +group("common_deps") { + deps = [ + "$pulseaudio_build_path/src:pulsecommon", + "$pulseaudio_build_path/src/pulsecore:pulsecore", + ] +} + +config("common_config") { + visibility = [ ":*" ] + + include_dirs = [ + "$pulseaudio_dir/src/modules", + "$pulseaudio_dir/src", + "$pulseaudio_dir", + "$pulseaudio_build_path/src", + "$pulseaudio_build_path/include", + ] + + cflags = [ + "-Wall", + "-Werror", + "-DHAVE_CONFIG_H", + "-D_GNU_SOURCE", + "-D__INCLUDED_FROM_PULSE_AUDIO", + ] +} + +ohos_shared_library("module-native-protocol-fd") { + sources = [ "$pulseaudio_dir/src/modules/module-native-protocol-fd.c" ] + cflags = [ "-DPA_MODULE_NAME=libmodule_native_protocol_fd_z_so" ] + ldflags = [ + "-Wl", + "--no-undefined", + ] + configs = [ ":common_config" ] + deps = [ + ":common_deps", + "$pulseaudio_build_path/src/pulsecore:protocol-native", + ] + + subsystem_name = "multimedia" + part_name = "multimedia_audio_standard" +} + +ohos_shared_library("module-native-protocol-tcp") { + sources = [ "$pulseaudio_dir/src/modules/module-protocol-stub.c" ] + cflags = [ + "-DPA_MODULE_NAME=libmodule_native_protocol_tcp_z_so", + "-DUSE_PROTOCOL_NATIVE", + "-DUSE_TCP_SOCKETS", + ] + ldflags = [ + "-Wl", + "--no-undefined", + ] + configs = [ ":common_config" ] + deps = [ + ":common_deps", + "$pulseaudio_build_path/src/pulsecore:protocol-native", + ] + + subsystem_name = "multimedia" + part_name = "multimedia_audio_standard" +} + +ohos_shared_library("module-native-protocol-unix") { + sources = [ "$pulseaudio_dir/src/modules/module-protocol-stub.c" ] + cflags = [ + "-DPA_MODULE_NAME=libmodule_native_protocol_unix_z_so", + "-DUSE_PROTOCOL_NATIVE", + "-DUSE_UNIX_SOCKETS", + ] + ldflags = [ + "-Wl", + "--no-undefined", + ] + configs = [ ":common_config" ] + deps = [ + ":common_deps", + "$pulseaudio_build_path/src/pulsecore:protocol-native", + ] + + subsystem_name = "multimedia" + part_name = "multimedia_audio_standard" +} + +ohos_shared_library("module-cli-protocol-unix") { + sources = [ "$pulseaudio_dir/src/modules/module-protocol-stub.c" ] + cflags = [ + "-DPA_MODULE_NAME=libmodule_cli_protocol_unix_z_so", + "-DUSE_PROTOCOL_CLI", + "-DUSE_UNIX_SOCKETS", + ] + ldflags = [ + "-Wl", + "--no-undefined", + ] + configs = [ ":common_config" ] + deps = [ + ":common_deps", + "$pulseaudio_build_path/src/pulsecore:protocol-cli", + ] + + subsystem_name = "multimedia" + part_name = "multimedia_audio_standard" +} + +ohos_shared_library("module-pipe-sink") { + sources = [ "$pulseaudio_dir/src/modules/module-pipe-sink.c" ] + cflags = [ "-DPA_MODULE_NAME=libmodule_pipe_sink_z_so" ] + ldflags = [ + "-Wl", + "--no-undefined", + ] + configs = [ ":common_config" ] + deps = [ ":common_deps" ] + + subsystem_name = "multimedia" + part_name = "multimedia_audio_standard" +} + +ohos_shared_library("module-pipe-source") { + sources = ["$pulseaudio_dir/src/modules/module-pipe-source.c",] + cflags = [ "-DPA_MODULE_NAME=libmodule_pipe_source_z_so" ] + + ldflags = [ + "-Wl", + "--no-undefined", + ] + + configs = [ ":common_config" ] + deps = [ ":common_deps" ] + + subsystem_name = "multimedia" + part_name = "multimedia_audio_standard" +} + +ohos_shared_library("module-suspend-on-idle") { + sources = ["$pulseaudio_dir/src/modules/module-suspend-on-idle.c",] + cflags = [ "-DPA_MODULE_NAME=libmodule_suspend_on_idle_z_so" ] + + ldflags = [ + "-Wl", + "--no-undefined", + ] + + configs = [ ":common_config" ] + deps = [ ":common_deps" ] + + subsystem_name = "multimedia" + part_name = "multimedia_audio_standard" +} diff --git a/frameworks/innerkitsimpl/pulseaudio/src/modules/hdi/BUILD.gn b/frameworks/innerkitsimpl/pulseaudio/src/modules/hdi/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..83fcf98202872511e159352036231b9992ffb3ef --- /dev/null +++ b/frameworks/innerkitsimpl/pulseaudio/src/modules/hdi/BUILD.gn @@ -0,0 +1,96 @@ +# Copyright (C) 2021 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") + +pulseaudio_dir = "//third_party/pulseaudio" +pulseaudio_build_path = "//foundation/multimedia/audio_standard/frameworks/innerkitsimpl/pulseaudio" + +config("hdi_config") { + visibility = [ ":*" ] + + include_dirs = [ + "$pulseaudio_dir/src", + "$pulseaudio_dir", + "$pulseaudio_build_path/src", + "$pulseaudio_build_path/include", + "//drivers/peripheral/audio/interfaces/include", + "//foundation/multimedia/audio_standard/frameworks/innerkitsimpl/common/include", + "//foundation/multimedia/audio_standard/frameworks/innerkitsimpl/audiocapturer/include", + "//foundation/multimedia/audio_standard/frameworks/innerkitsimpl/audiorenderer/include", + ] + + cflags = [ + "-Wall", + "-Werror", + "-DHAVE_CONFIG_H", + "-D_GNU_SOURCE", + "-D__INCLUDED_FROM_PULSE_AUDIO", + ] +} + +ohos_shared_library("module-hdi-sink") { + sources = [ + "$pulseaudio_build_path/src/modules/hdi/hdi_sink.c", + "$pulseaudio_build_path/src/modules/hdi/module_hdi_sink.c" + ] + + configs = [ ":hdi_config" ] + + cflags = [ + "-DPA_MODULE_NAME=libmodule_hdi_sink_z_so", + ] + + ldflags = [ + "-Wl", + "--no-undefined", + ] + + deps = [ + "$pulseaudio_build_path/src/pulse:pulse", + "$pulseaudio_build_path/src/pulsecore:pulsecore", + "$pulseaudio_build_path/src:pulsecommon", + "//foundation/multimedia/audio_standard/frameworks/innerkitsimpl/audiorenderer:audio_renderer_sink", + ] + + subsystem_name = "multimedia" + part_name = "multimedia_audio_standard" +} + +ohos_shared_library("module-hdi-source") { + sources = [ + "hdi_source.c", + "module_hdi_source.c" + ] + + configs = [ ":hdi_config" ] + + cflags = [ + "-DPA_MODULE_NAME=libmodule_hdi_source_z_so", + ] + + deps = [ + "$pulseaudio_build_path/src/pulse:pulse", + "$pulseaudio_build_path/src/pulsecore:pulsecore", + "$pulseaudio_build_path/src:pulsecommon", + "//third_party/bounds_checking_function:libsec_static", + "//foundation/multimedia/audio_standard/frameworks/innerkitsimpl/audiocapturer:audio_capturer_source", + ] + + external_deps = [ + "hiviewdfx_hilog_native:libhilog", + ] + + subsystem_name = "multimedia" + part_name = "multimedia_audio_standard" +} diff --git a/frameworks/innerkitsimpl/pulseaudio/src/modules/hdi/hdi_sink.c b/frameworks/innerkitsimpl/pulseaudio/src/modules/hdi/hdi_sink.c new file mode 100644 index 0000000000000000000000000000000000000000..cc9f7406568106102c0bc2595c266eae997972a1 --- /dev/null +++ b/frameworks/innerkitsimpl/pulseaudio/src/modules/hdi/hdi_sink.c @@ -0,0 +1,473 @@ +/* + * Copyright (C) 2021 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. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#define DEFAULT_SINK_NAME "hdi_output" +#define DEFAULT_AUDIO_DEVICE_NAME "Speaker" +#define DEFAULT_BUFFER_SIZE 8192 +#define MAX_SINK_VOLUME_LEVEL 1.0 + +struct Userdata { + size_t buffer_size; + size_t bytes_dropped; + pa_thread_mq thread_mq; + pa_memchunk memchunk; + pa_usec_t block_usec; + pa_usec_t timestamp; + pa_thread *thread; + pa_rtpoll *rtpoll; + pa_core *core; + pa_module *module; + pa_sink *sink; + pa_sample_spec ss; + pa_channel_map map; + bool isHDISinkInitialized; +}; + +static void UserdataFree(struct Userdata *u); +static int32_t PrepareDevice(const pa_sample_spec *ss); + +static ssize_t RenderWrite(pa_memchunk *pchunk) +{ + size_t index, length; + ssize_t count = 0; + void *p = NULL; + int32_t ret; + + pa_assert(pchunk); + + index = pchunk->index; + length = pchunk->length; + p = pa_memblock_acquire(pchunk->memblock); + pa_assert(p); + + while (true) { + uint64_t writeLen = 0; + + ret = AudioRendererRenderFrame((char *) p + index, (uint64_t)length, &writeLen); + if (writeLen > length) { + pa_log_error("Error writeLen > actual bytes. Length: %u, Written: %llu bytes, %d ret", + length, writeLen, ret); + count = -1 - count; + break; + } + if (writeLen == 0) { + pa_log_error("Failed to render Length: %u, Written: %llu bytes, %d ret", + length, writeLen, ret); + count = -1 - count; + break; + } else { + pa_log_info("Success: outputting to audio renderer Length: %u, Written: %llu bytes, %d ret", + length, writeLen, ret); + count += writeLen; + index += writeLen; + length -= writeLen; + pa_log_info("Remaining bytes Length: %u", length); + if (length <= 0) { + break; + } + } + } + pa_memblock_release(pchunk->memblock); + + return count; +} + +static void ProcessRenderUseTiming(struct Userdata *u, pa_usec_t now) +{ + size_t dropped; + size_t consumed = 0; + + pa_assert(u); + + // Fill the buffer up the latency size + while (u->timestamp < now + u->block_usec) { + ssize_t written = 0; + pa_memchunk chunk; + + pa_sink_render(u->sink, u->sink->thread_info.max_request, &chunk); + + pa_assert(chunk.length > 0); + + if ((written = RenderWrite(&chunk)) < 0) + written = -1 - written; + + pa_memblock_unref(chunk.memblock); + + u->timestamp += pa_bytes_to_usec(chunk.length, &u->sink->sample_spec); + + dropped = chunk.length - written; + + if (u->bytes_dropped != 0 && dropped != chunk.length) { + pa_log_debug("HDI-sink continuously dropped %zu bytes", u->bytes_dropped); + u->bytes_dropped = 0; + } + + if (u->bytes_dropped == 0 && dropped != 0) + pa_log_debug("HDI-sink just dropped %zu bytes", dropped); + + u->bytes_dropped += dropped; + + consumed += chunk.length; + + if (consumed >= u->sink->thread_info.max_request) + break; + } +} + +static void ThreadFuncUseTiming(void *userdata) +{ + struct Userdata *u = userdata; + + pa_assert(u); + + pa_log_debug("Thread (use timing) starting up"); + pa_thread_mq_install(&u->thread_mq); + + u->timestamp = pa_rtclock_now(); + + while (true) { + pa_usec_t now = 0; + int ret; + + if (PA_SINK_IS_OPENED(u->sink->thread_info.state)) + now = pa_rtclock_now(); + + if (PA_UNLIKELY(u->sink->thread_info.rewind_requested)) + pa_sink_process_rewind(u->sink, 0); + + // Render some data and drop it immediately + if (PA_SINK_IS_OPENED(u->sink->thread_info.state)) { + if (u->timestamp <= now) + ProcessRenderUseTiming(u, now); + + pa_rtpoll_set_timer_absolute(u->rtpoll, u->timestamp); + } else + pa_rtpoll_set_timer_disabled(u->rtpoll); + + // Hmm, nothing to do. Let's sleep + if ((ret = pa_rtpoll_run(u->rtpoll)) < 0) + goto fail; + + if (ret == 0) { + goto finish; + } + } + +fail: + // If this was no regular exit from the loop we have to continue + // processing messages until we received PA_MESSAGE_SHUTDOWN + pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, + u->module, 0, NULL, NULL); + pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN); + +finish: + pa_log_debug("Thread (use timing) shutting down"); +} + +static void SinkUpdateRequestedLatencyCb(pa_sink *s) +{ + struct Userdata *u = NULL; + size_t nbytes; + + pa_sink_assert_ref(s); + pa_assert_se(u = s->userdata); + + u->block_usec = pa_sink_get_requested_latency_within_thread(s); + + if (u->block_usec == (pa_usec_t) - 1) + u->block_usec = s->thread_info.max_latency; + + nbytes = pa_usec_to_bytes(u->block_usec, &s->sample_spec); + pa_sink_set_max_request_within_thread(s, nbytes); +} + +// Called from IO context +static int SinkProcessMsg(pa_msgobject *o, int code, void *data, int64_t offset, + pa_memchunk *chunk) +{ + struct Userdata *u = PA_SINK(o)->userdata; + switch (code) { + case PA_SINK_MESSAGE_GET_LATENCY: { + int64_t latency; + uint32_t hdiLatency; + + // Tries to fetch latency from HDI else will make an estimate based + // on samples to be rendered based on the timestamp and current time + if (AudioRendererSinkGetLatency(&hdiLatency) == 0) { + latency = (PA_USEC_PER_MSEC * hdiLatency); + } else { + pa_usec_t now = pa_rtclock_now(); + latency = (now - u->timestamp); + } + + *((int64_t *)data) = latency; + return 0; + } + default: + break; + } + return pa_sink_process_msg(o, code, data, offset, chunk); +} + +// Called from the IO thread. +static int SinkSetStateInIoThreadCb(pa_sink *s, pa_sink_state_t newState, + pa_suspend_cause_t newSuspendCause) +{ + struct Userdata *u = NULL; + + pa_assert(s); + pa_assert_se(u = s->userdata); + + if (s->thread_info.state == PA_SINK_SUSPENDED || s->thread_info.state == PA_SINK_INIT) { + if (PA_SINK_IS_OPENED(newState)) { + u->timestamp = pa_rtclock_now(); + if (!u->isHDISinkInitialized) { + pa_log("Reinitializing HDI rendering device with rate: %d, channels: %d", u->ss.rate, u->ss.channels); + if (PrepareDevice(&u->ss) < 0) { + pa_log_error("HDI renderer reinitialization failed"); + } else { + u->isHDISinkInitialized = true; + pa_log("Successfully reinitialized HDI renderer"); + } + } + } + } else if (PA_SINK_IS_OPENED(s->thread_info.state)) { + if (newState == PA_SINK_SUSPENDED) { + // Continuously dropping data (clear counter on entering suspended state. + if (u->bytes_dropped != 0) { + pa_log_debug("HDI-sink continuously dropping data - clear statistics (%zu -> 0 bytes dropped)", + u->bytes_dropped); + u->bytes_dropped = 0; + } + if (u->isHDISinkInitialized) { + AudioRendererSinkStop(); + AudioRendererSinkDeInit(); + u->isHDISinkInitialized = false; + pa_log("Deinitialized HDI renderer"); + } + } + } + + return 0; +} + +static int32_t PrepareDevice(const pa_sample_spec *ss) +{ + AudioSinkAttr sample_attrs; + int32_t ret; + + sample_attrs.format = AUDIO_FORMAT_PCM_16_BIT; + sample_attrs.sampleRate = ss->rate; + sample_attrs.channel = ss->channels; + sample_attrs.volume = MAX_SINK_VOLUME_LEVEL; + + ret = AudioRendererSinkInit(&sample_attrs); + if (ret != 0) { + pa_log_error("audiorenderer Init failed!"); + return -1; + } + + ret = AudioRendererSinkStart(); + if (ret != 0) { + pa_log_error("audiorenderer control start failed!"); + AudioRendererSinkDeInit(); + return -1; + } + + ret = AudioRendererSinkSetVolume(MAX_SINK_VOLUME_LEVEL, MAX_SINK_VOLUME_LEVEL); + if (ret != 0) { + pa_log_error("audiorenderer set volume failed!"); + AudioRendererSinkStop(); + AudioRendererSinkDeInit(); + return -1; + } + + return 0; +} + +static pa_sink* PaHdiSinkInit(struct Userdata *u, pa_modargs *ma, const char *driver) +{ + pa_sink_new_data data; + pa_module *m; + pa_sink *sink = NULL; + + m = u->module; + u->ss = m->core->default_sample_spec; + u->map = m->core->default_channel_map; + if (pa_modargs_get_sample_spec_and_channel_map(ma, &u->ss, &u->map, PA_CHANNEL_MAP_DEFAULT) < 0) { + pa_log("Failed to parse sample specification and channel map"); + goto fail; + } + + pa_log("Initializing HDI rendering device with rate: %d, channels: %d", u->ss.rate, u->ss.channels); + if (PrepareDevice(&u->ss) < 0) + goto fail; + + u->isHDISinkInitialized = true; + pa_log("Initialization of HDI rendering device completed"); + pa_sink_new_data_init(&data); + data.driver = driver; + data.module = m; + + pa_sink_new_data_set_name(&data, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME)); + pa_sink_new_data_set_sample_spec(&data, &u->ss); + pa_sink_new_data_set_channel_map(&data, &u->map); + pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, DEFAULT_AUDIO_DEVICE_NAME); + pa_proplist_setf(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "HDI sink is %s", + DEFAULT_AUDIO_DEVICE_NAME); + + if (pa_modargs_get_proplist(ma, "sink_properties", data.proplist, PA_UPDATE_REPLACE) < 0) { + pa_log("Invalid properties"); + pa_sink_new_data_done(&data); + goto fail; + } + + sink = pa_sink_new(m->core, &data, + PA_SINK_HARDWARE | PA_SINK_LATENCY | PA_SINK_DYNAMIC_LATENCY); + pa_sink_new_data_done(&data); + + return sink; + +fail: + return NULL; +} + +pa_sink *PaHdiSinkNew(pa_module *m, pa_modargs *ma, const char *driver) +{ + struct Userdata *u = NULL; + char *threadName = NULL; + + pa_assert(m); + pa_assert(ma); + + u = pa_xnew0(struct Userdata, 1); + u->core = m->core; + u->module = m; + + pa_memchunk_reset(&u->memchunk); + u->rtpoll = pa_rtpoll_new(); + + if (pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll) < 0) { + pa_log("pa_thread_mq_init() failed."); + goto fail; + } + + u->sink = PaHdiSinkInit(u, ma, driver); + if (!u->sink) { + pa_log("Failed to create sink object"); + goto fail; + } + + u->sink->parent.process_msg = SinkProcessMsg; + u->sink->set_state_in_io_thread = SinkSetStateInIoThreadCb; + u->sink->update_requested_latency = SinkUpdateRequestedLatencyCb; + u->sink->userdata = u; + + pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq); + pa_sink_set_rtpoll(u->sink, u->rtpoll); + + u->bytes_dropped = 0; + u->buffer_size = DEFAULT_BUFFER_SIZE; + if (pa_modargs_get_value_u32(ma, "buffer_size", &u->buffer_size) < 0) { + pa_log("Failed to parse buffer_size argument."); + goto fail; + } + + u->block_usec = pa_bytes_to_usec(u->buffer_size, &u->sink->sample_spec); + pa_sink_set_latency_range(u->sink, 0, u->block_usec); + pa_sink_set_max_request(u->sink, u->buffer_size); + + threadName = pa_sprintf_malloc("hdi-sink-playback"); + if (!(u->thread = pa_thread_new(threadName, ThreadFuncUseTiming, u))) { + pa_log("Failed to create thread."); + goto fail; + } + pa_xfree(threadName); + threadName = NULL; + + pa_sink_put(u->sink); + + return u->sink; +fail: + pa_xfree(threadName); + + if (u) { + UserdataFree(u); + } + + return NULL; +} + +static void UserdataFree(struct Userdata *u) +{ + pa_assert(u); + + if (u->sink) + pa_sink_unlink(u->sink); + + if (u->thread) { + pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL); + pa_thread_free(u->thread); + } + + pa_thread_mq_done(&u->thread_mq); + + if (u->sink) + pa_sink_unref(u->sink); + + if (u->memchunk.memblock) + pa_memblock_unref(u->memchunk.memblock); + + if (u->rtpoll) + pa_rtpoll_free(u->rtpoll); + + AudioRendererSinkStop(); + AudioRendererSinkDeInit(); + + pa_xfree(u); +} + +void PaHdiSinkFree(pa_sink *s) +{ + struct Userdata *u = NULL; + + pa_sink_assert_ref(s); + pa_assert_se(u = s->userdata); + + UserdataFree(u); +} diff --git a/frameworks/innerkitsimpl/pulseaudio/src/modules/hdi/hdi_source.c b/frameworks/innerkitsimpl/pulseaudio/src/modules/hdi/hdi_source.c new file mode 100644 index 0000000000000000000000000000000000000000..9b14c5f875342610af5a76cde062fd44c9ee38a4 --- /dev/null +++ b/frameworks/innerkitsimpl/pulseaudio/src/modules/hdi/hdi_source.c @@ -0,0 +1,424 @@ +/* + * Copyright (C) 2021 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. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include "media_log.h" + +#define DEFAULT_SOURCE_NAME "hdi_input" +#define DEFAULT_AUDIO_DEVICE_NAME "Internal Mic" + +#define DEFAULT_BUFFER_SIZE (1024 * 16) +#define MAX_VOLUME_VALUE 15.0 +#define DEFAULT_LEFT_VOLUME MAX_VOLUME_VALUE +#define DEFAULT_RIGHT_VOLUME MAX_VOLUME_VALUE +#define MAX_LATENCY_USEC (PA_USEC_PER_SEC * 2) +#define MIN_LATENCY_USEC 500 +#define AUDIO_POINT_NUM 1024 +#define AUDIO_FRAME_NUM_IN_BUF 30 + +struct userdata { + pa_core *core; + pa_module *module; + pa_source *source; + pa_thread *thread; + pa_thread_mq thread_mq; + pa_rtpoll *rtpoll; + size_t buffer_size; + pa_usec_t block_usec; + pa_usec_t timestamp; + AudioSourceAttr attrs; + // A flag to signal us to prevent silent record during bootup + bool IsReady; + bool IsCapturerInit; +}; + +static int pa_capturer_init(struct userdata *u); +static void pa_capturer_exit(void); + +static void userdata_free(struct userdata *u) { + pa_assert(u); + if (u->source) + pa_source_unlink(u->source); + + if (u->thread) { + pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL); + pa_thread_free(u->thread); + } + + pa_thread_mq_done(&u->thread_mq); + + if (u->source) + pa_source_unref(u->source); + + if (u->rtpoll) + pa_rtpoll_free(u->rtpoll); + + AudioCapturerSourceStop(); + AudioCapturerSourceDeInit(); + pa_xfree(u); +} + +static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) { + struct userdata *u = PA_SOURCE(o)->userdata; + + switch (code) { + case PA_SOURCE_MESSAGE_GET_LATENCY: { + pa_usec_t now; + now = pa_rtclock_now(); + *((int64_t*) data) = (int64_t)now - (int64_t)u->timestamp; + return 0; + } + default: { + pa_log("source_process_msg default case"); + return pa_source_process_msg(o, code, data, offset, chunk); + } + } +} + +/* Called from the IO thread. */ +static int source_set_state_in_io_thread_cb(pa_source *s, pa_source_state_t new_state, pa_suspend_cause_t new_suspend_cause) { + struct userdata *u = NULL; + pa_assert(s); + pa_assert_se(u = s->userdata); + if (s->thread_info.state == PA_SOURCE_SUSPENDED || s->thread_info.state == PA_SOURCE_INIT) { + if (PA_SOURCE_IS_OPENED(new_state)) + u->timestamp = pa_rtclock_now(); + } + + return 0; +} + +static pa_hook_result_t source_output_fixate_hook_cb(pa_core *c, pa_source_output_new_data *data, + struct userdata *u) { + int ret; + MEDIA_DEBUG_LOG("HDI Source: Detected source output"); + pa_assert(data); + pa_assert(u); + + if (!strcmp(u->source->name, data->source->name)) { + // Signal Ready when a Source Output is connected + if (!u->IsReady) + u->IsReady = true; + + ret = pa_capturer_init(u); + if (ret != 0) { + MEDIA_DEBUG_LOG("HDI Source: Cannot initialize Capturer! ret=%d", ret); + return PA_HOOK_OK; + } + + u->timestamp = pa_rtclock_now(); + pa_source_suspend(u->source, false, PA_SUSPEND_IDLE); + } + + return PA_HOOK_OK; +} + +static pa_hook_result_t source_output_move_finish_hook_cb(pa_core *c, pa_source_output *output, struct userdata *u) { + int ret; + + MEDIA_DEBUG_LOG("HDI Source: Detected source move finish"); + pa_assert(output); + pa_assert(u); + + if (!strcmp(output->source->name, u->source->name)) { + // Signal Ready when a source output is connected + if (!u->IsReady) + u->IsReady = true; + + ret = pa_capturer_init(u); + if (ret != 0) { + pa_log_error("HDI Source: Cannot initialize capturer! ret=%d", ret); + return PA_HOOK_OK; + } + + u->timestamp = pa_rtclock_now(); + pa_source_suspend(u->source, false, PA_SUSPEND_IDLE); + } + + return PA_HOOK_OK; +} + +static void thread_func(void *userdata) { + struct userdata *u = userdata; + bool timer_elapsed = false; + + pa_assert(u); + + if (u->core->realtime_scheduling) + pa_thread_make_realtime(u->core->realtime_priority); + + pa_thread_mq_install(&u->thread_mq); + u->timestamp = pa_rtclock_now(); + MEDIA_DEBUG_LOG("HDI Source: u->timestamp : %{public}llu", u->timestamp); + + while(true) { + int ret = 0; + uint64_t requestBytes; + uint64_t replyBytes = 0; + void *p = NULL; + + if (PA_SOURCE_IS_OPENED(u->source->thread_info.state) && + (u->source->thread_info.state != PA_SOURCE_SUSPENDED) && u->IsReady && u->IsCapturerInit) { + pa_usec_t now; + pa_memchunk chunk; + + now = pa_rtclock_now(); + MEDIA_DEBUG_LOG("HDI Source: now: %{public}llu timer_elapsed: %{public}d", now, timer_elapsed); + + if (timer_elapsed && (chunk.length = pa_usec_to_bytes(now - u->timestamp, &u->source->sample_spec)) > 0) { + chunk.length = u->buffer_size; + MEDIA_DEBUG_LOG("HDI Source: chunk.length = u->buffer_size: %{public}zu", chunk.length); + chunk.memblock = pa_memblock_new(u->core->mempool, chunk.length); + pa_assert(chunk.memblock); + p = pa_memblock_acquire(chunk.memblock); + pa_assert(p); + + requestBytes = pa_memblock_get_length(chunk.memblock); + AudioCapturerSourceFrame((char *) p, (uint64_t)requestBytes, &replyBytes); + + pa_memblock_release(chunk.memblock); + MEDIA_DEBUG_LOG("HDI Source: request bytes: %{public}llu, replyBytes: %{public}llu", requestBytes, replyBytes); + if(replyBytes > requestBytes) { + MEDIA_ERR_LOG("HDI Source: Error replyBytes > requestBytes. Requested data Length: %{public}llu, Read: %{public}llu bytes, %{public}d ret", requestBytes, replyBytes, ret); + pa_memblock_unref(chunk.memblock); + break; + } + + if (replyBytes == 0) { + MEDIA_ERR_LOG("HDI Source: Failed to read, Requested data Length: %{public}llu bytes," + " Read: %{public}llu bytes, %{public}d ret", requestBytes, replyBytes, ret); + pa_memblock_unref(chunk.memblock); + break; + } + + chunk.index = 0; + chunk.length = replyBytes; + pa_source_post(u->source, &chunk); + pa_memblock_unref(chunk.memblock); + u->timestamp += pa_bytes_to_usec(chunk.length, &u->source->sample_spec); + MEDIA_INFO_LOG("HDI Source: new u->timestamp : %{public}llu", u->timestamp); + } + + pa_rtpoll_set_timer_absolute(u->rtpoll, u->timestamp + u->block_usec); + } else { + if (u->IsCapturerInit) { + u->IsCapturerInit = false; + pa_capturer_exit(); + MEDIA_INFO_LOG("HDI Source: Capturer exit done"); + } + + pa_rtpoll_set_timer_disabled(u->rtpoll); + MEDIA_INFO_LOG("HDI Source: pa_rtpoll_set_timer_disabled done "); + } + + /* Hmm, nothing to do. Let's sleep */ + if ((ret = pa_rtpoll_run(u->rtpoll)) < 0) { + /* If this was no regular exit from the loop we have to continue + * processing messages until we received PA_MESSAGE_SHUTDOWN */ + MEDIA_INFO_LOG("HDI Source: pa_rtpoll_run ret:%{public}d failed", ret ); + pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, + 0, NULL, NULL); + pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN); + return; + } + + timer_elapsed = pa_rtpoll_timer_elapsed(u->rtpoll); + + if (ret == 0) { + return; + } + } +} + +static int pa_capturer_init(struct userdata *u) { + int ret; + + ret = AudioCapturerSourceInit(&u->attrs); + if (ret != 0) { + MEDIA_INFO_LOG("Audio capturer init failed!"); + return ret; + } + + ret = AudioCapturerSourceStart(); + if (ret != 0) { + MEDIA_INFO_LOG("Audio capturer start failed!"); + goto fail; + } + + ret = AudioCapturerSourceSetVolume(DEFAULT_LEFT_VOLUME, DEFAULT_RIGHT_VOLUME); + if (ret != 0) { + MEDIA_INFO_LOG("audio capturer set volume failed!"); + goto fail; + } + + u->IsCapturerInit = true; + return ret; + +fail: + pa_capturer_exit(); + return ret; +} + +static void pa_capturer_exit() { + AudioCapturerSourceStop(); + AudioCapturerSourceDeInit(); +} + +pa_source *pa_hdi_source_new(pa_module *m, pa_modargs *ma, const char*driver) { + struct userdata *u = NULL; + pa_sample_spec ss; + char *thread_name = NULL; + pa_channel_map map; + pa_source_new_data data; + int ret; + + pa_assert(m); + pa_assert(ma); + + ss = m->core->default_sample_spec; + map = m->core->default_channel_map; + + /* Override with modargs if provided */ + if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_DEFAULT) < 0) { + MEDIA_INFO_LOG("Failed to parse sample specification and channel map"); + goto fail; + } + + u = pa_xnew0(struct userdata, 1); + u->core = m->core; + u->module = m; + u->rtpoll = pa_rtpoll_new(); + + // Set IsReady to false at start. will be made true when a Source Output is connected + u->IsReady = false; + + if (pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll) < 0) { + MEDIA_INFO_LOG("pa_thread_mq_init() failed."); + goto fail; + } + + u->buffer_size = DEFAULT_BUFFER_SIZE; + u->attrs.format = AUDIO_FORMAT_PCM_16_BIT; + u->attrs.channel = ss.channels; + u->attrs.sampleRate = ss.rate; + MEDIA_INFO_LOG("AudioDeviceCreateCapture format: %{public}d, channel: %{public}d, sampleRate: %{public}d", + u->attrs.format, u->attrs.channel, u->attrs.sampleRate); + ret = pa_capturer_init(u); + if (ret != 0) { + goto fail; + } + + pa_source_new_data_init(&data); + data.driver = __FILE__; + data.module = m; + pa_source_new_data_set_name(&data, pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME)); + pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, DEFAULT_AUDIO_DEVICE_NAME); + pa_proplist_setf(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "HDI source %s", DEFAULT_AUDIO_DEVICE_NAME); + pa_source_new_data_set_sample_spec(&data, &ss); + pa_source_new_data_set_channel_map(&data, &map); + pa_proplist_setf(data.proplist, PA_PROP_DEVICE_BUFFERING_BUFFER_SIZE, "%lu", (unsigned long)u->buffer_size); + + if (pa_modargs_get_proplist(ma, "source_properties", data.proplist, PA_UPDATE_REPLACE) < 0) { + MEDIA_INFO_LOG("Invalid properties"); + pa_source_new_data_done(&data); + goto fail; + } + + u->source = pa_source_new(m->core, &data, PA_SOURCE_HARDWARE | PA_SOURCE_LATENCY); + pa_source_new_data_done(&data); + + if (!u->source) { + MEDIA_INFO_LOG("Failed to create source object"); + goto fail; + } + + u->source->parent.process_msg = source_process_msg; + u->source->set_state_in_io_thread = source_set_state_in_io_thread_cb; + u->source->userdata = u; + + pa_source_set_asyncmsgq(u->source, u->thread_mq.inq); + pa_source_set_rtpoll(u->source, u->rtpoll); + + if (pa_modargs_get_value_u32(ma, "buffer_size", &u->buffer_size) < 0) { + MEDIA_INFO_LOG("Failed to parse buffer_size argument."); + goto fail; + } + + u->block_usec = pa_bytes_to_usec(u->buffer_size, &u->source->sample_spec); + pa_source_set_latency_range(u->source, 0, u->block_usec); + u->source->thread_info.max_rewind = pa_usec_to_bytes(u->block_usec, &u->source->sample_spec); + + pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_FIXATE], PA_HOOK_NORMAL, + (pa_hook_cb_t) source_output_fixate_hook_cb, u); + pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_FINISH], PA_HOOK_NORMAL, + (pa_hook_cb_t) source_output_move_finish_hook_cb, u); + + thread_name = pa_sprintf_malloc("hdi-source-record"); + if (!(u->thread = pa_thread_new(thread_name, thread_func, u))) { + MEDIA_INFO_LOG("Failed to create hdi-source-record thread!"); + goto fail; + } + + pa_xfree(thread_name); + thread_name = NULL; + pa_source_put(u->source); + return u->source; + +fail: + pa_xfree(thread_name); + + if (u->IsCapturerInit) + pa_capturer_exit(); + + if (u) + userdata_free(u); + + return NULL; +} + +void pa_hdi_source_free(pa_source *s) { + struct userdata *u; + pa_source_assert_ref(s); + pa_assert_se(u = s->userdata); + userdata_free(u); +} diff --git a/frameworks/innerkitsimpl/pulseaudio/src/modules/hdi/module_hdi_sink.c b/frameworks/innerkitsimpl/pulseaudio/src/modules/hdi/module_hdi_sink.c new file mode 100644 index 0000000000000000000000000000000000000000..f47aa9ed2fa57d4186563edcf4f75a1bed39db3a --- /dev/null +++ b/frameworks/innerkitsimpl/pulseaudio/src/modules/hdi/module_hdi_sink.c @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2021 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. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include + +pa_sink* PaHdiSinkNew(pa_module *m, pa_modargs *ma, const char *driver); +void PaHdiSinkFree(pa_sink *s); + +PA_MODULE_AUTHOR("OpenHarmony"); +PA_MODULE_DESCRIPTION("OpenHarmony HDI Sink"); +PA_MODULE_VERSION(PACKAGE_VERSION); +PA_MODULE_LOAD_ONCE(false); +PA_MODULE_USAGE( + "sink_name= " + "sink_properties= " + "format= " + "rate= " + "channels= " + "channel_map= " + "buffer_size=" + ); + +static const char * const VALID_MODARGS[] = { + "sink_name", + "sink_properties", + "format", + "rate", + "channels", + "channel_map", + "buffer_size", + NULL +}; + +int pa__init(pa_module *m) +{ + pa_modargs *ma = NULL; + + pa_assert(m); + + if (!(ma = pa_modargs_new(m->argument, VALID_MODARGS))) { + pa_log("Failed to parse module arguments"); + goto fail; + } + + if (!(m->userdata = PaHdiSinkNew(m, ma, __FILE__))) { + goto fail; + } + + pa_modargs_free(ma); + + return 0; + +fail: + if (ma) { + pa_modargs_free(ma); + } + + pa__done(m); + + return -1; +} + +int pa__get_n_used(pa_module *m) +{ + pa_sink *sink = NULL; + + pa_assert(m); + pa_assert_se(sink = m->userdata); + + return pa_sink_linked_by(sink); +} + +void pa__done(pa_module *m) +{ + pa_sink *sink = NULL; + + pa_assert(m); + + if ((sink = m->userdata)) { + PaHdiSinkFree(sink); + } +} diff --git a/frameworks/innerkitsimpl/pulseaudio/src/modules/hdi/module_hdi_source.c b/frameworks/innerkitsimpl/pulseaudio/src/modules/hdi/module_hdi_source.c new file mode 100644 index 0000000000000000000000000000000000000000..4e8760c937f138f9ad2e11f8f5130fffe858de00 --- /dev/null +++ b/frameworks/innerkitsimpl/pulseaudio/src/modules/hdi/module_hdi_source.c @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2021 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. +*/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include +#include +#include +#include + +pa_source* pa_hdi_source_new(pa_module *m, pa_modargs *ma, const char*driver); + +void pa_hdi_source_free(pa_source *s); + +PA_MODULE_AUTHOR("OpenHarmony"); +PA_MODULE_DESCRIPTION("OpenHarmony HDI Source"); +PA_MODULE_VERSION(PACKAGE_VERSION); +PA_MODULE_LOAD_ONCE(false); +PA_MODULE_USAGE( + "source_name= " + "source_properties= " + "format= " + "rate= " + "channels= " + "channel_map=" + "buffer_size=" + ); + +static const char * const VALID_MODARGS[] = { + "source_name", + "source_properties", + "format", + "rate", + "channels", + "channel_map", + "buffer_size", + NULL +}; + +int pa__init(pa_module *m) +{ + pa_modargs *ma = NULL; + + pa_assert(m); + + if (!(ma = pa_modargs_new(m->argument, VALID_MODARGS))) { + pa_log("Failed to parse module arguments"); + goto fail; + } + + if (!(m->userdata = pa_hdi_source_new(m, ma, __FILE__))) + goto fail; + + pa_modargs_free(ma); + + return 0; + +fail: + + if (ma) + pa_modargs_free(ma); + + pa__done(m); + + return -1; +} + +int pa__get_n_used(pa_module *m) +{ + pa_source *source = NULL; + + pa_assert(m); + pa_assert_se(source = m->userdata); + + return pa_source_linked_by(source); +} + +void pa__done(pa_module*m) +{ + pa_source *source = NULL; + + pa_assert(m); + + if ((source = m->userdata)) + pa_hdi_source_free(source); +} diff --git a/frameworks/innerkitsimpl/pulseaudio/src/pulse/BUILD.gn b/frameworks/innerkitsimpl/pulseaudio/src/pulse/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..797695996b6c234a39a11963313bfd75d8aff35b --- /dev/null +++ b/frameworks/innerkitsimpl/pulseaudio/src/pulse/BUILD.gn @@ -0,0 +1,113 @@ +# Copyright (C) 2021 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") + +pulseaudio_dir = "//third_party/pulseaudio" +pulseaudio_build_path = "//foundation/multimedia/audio_standard/frameworks/innerkitsimpl/pulseaudio" + +config("pulse_config") { + visibility = [ ":*" ] + + include_dirs = [ + "$pulseaudio_dir/src/pulse", + "$pulseaudio_dir/src", + "$pulseaudio_dir", + "$pulseaudio_build_path/src", + "$pulseaudio_build_path/include", + "//third_party/glib/glib", + "//third_party/glib", + ] + + cflags = [ + "-Wall", + "-Werror", + "-Wno-implicit-function-declaration", + "-Wno-sign-compare", + "-Wno-unused-function", + "-DHAVE_CONFIG_H", + "-D_GNU_SOURCE", + ] +} + +ohos_source_set("pulse_sources") { + sources = [ + "$pulseaudio_dir/src/pulse/channelmap.c", + "$pulseaudio_dir/src/pulse/context.c", + "$pulseaudio_dir/src/pulse/direction.c", + "$pulseaudio_dir/src/pulse/error.c", + "$pulseaudio_dir/src/pulse/ext-device-manager.c", + "$pulseaudio_dir/src/pulse/ext-device-restore.c", + "$pulseaudio_dir/src/pulse/ext-stream-restore.c", + "$pulseaudio_dir/src/pulse/format.c", + "$pulseaudio_dir/src/pulse/internal.h", + "$pulseaudio_dir/src/pulse/introspect.c", + "$pulseaudio_dir/src/pulse/mainloop-api.c", + "$pulseaudio_dir/src/pulse/mainloop-signal.c", + "$pulseaudio_dir/src/pulse/mainloop.c", + "$pulseaudio_dir/src/pulse/operation.c", + "$pulseaudio_dir/src/pulse/proplist.c", + "$pulseaudio_dir/src/pulse/rtclock.c", + "$pulseaudio_dir/src/pulse/sample.c", + "$pulseaudio_dir/src/pulse/scache.c", + "$pulseaudio_dir/src/pulse/stream.c", + "$pulseaudio_dir/src/pulse/subscribe.c", + "$pulseaudio_dir/src/pulse/thread-mainloop.c", + "$pulseaudio_dir/src/pulse/timeval.c", + "$pulseaudio_dir/src/pulse/utf8.c", + "$pulseaudio_dir/src/pulse/util.c", + "$pulseaudio_build_path/src/pulse/ohos_pa_volume.c", + "$pulseaudio_dir/src/pulse/xmalloc.c", + ] + + configs = [ + ":pulse_config", + ] +} + +ohos_shared_library("pulse") { + deps = [ + ":pulse_sources", + "$pulseaudio_build_path/src:pulsecommon", + ] + + subsystem_name = "multimedia" + part_name = "multimedia_audio_standard" +} + +ohos_shared_library("pulse-simple") { + sources = [ "$pulseaudio_dir/src/pulse/simple.c" ] + + configs = [ ":pulse_config" ] + + deps = [ + "$pulseaudio_build_path/src:pulsecommon", + "$pulseaudio_build_path/src/pulse:pulse", + ] + + subsystem_name = "multimedia" + part_name = "multimedia_audio_standard" +} + +ohos_shared_library("pulse-mainloop-glib") { + sources = [ "$pulseaudio_build_path/src/pulse/ohos_glib-mainloop.c" ] + + configs = [ ":pulse_config" ] + deps = [ + "$pulseaudio_build_path/src:pulsecommon", + "//third_party/glib:glib", + ] + + subsystem_name = "multimedia" + part_name = "multimedia_audio_standard" +} diff --git a/frameworks/innerkitsimpl/pulseaudio/src/pulse/ohos_glib-mainloop.c b/frameworks/innerkitsimpl/pulseaudio/src/pulse/ohos_glib-mainloop.c new file mode 100644 index 0000000000000000000000000000000000000000..82c45713fd2bcdfd454b41dbceb9fa2a5c97ef7d --- /dev/null +++ b/frameworks/innerkitsimpl/pulseaudio/src/pulse/ohos_glib-mainloop.c @@ -0,0 +1,661 @@ +/*** + This file is part of PulseAudio. + + Copyright 2004-2006 Lennart Poettering + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, see . +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +#include +#include +#include + +#include +#include +#include "glib-mainloop.h" + +struct pa_io_event { + pa_glib_mainloop *mainloop; + int dead; + + GPollFD poll_fd; + int poll_fd_added; + + pa_io_event_cb_t callback; + void *userdata; + pa_io_event_destroy_cb_t destroy_callback; + + PA_LLIST_FIELDS(pa_io_event); +}; + +struct pa_time_event { + pa_glib_mainloop *mainloop; + int dead; + + int enabled; + struct timeval timeval; + + pa_time_event_cb_t callback; + void *userdata; + pa_time_event_destroy_cb_t destroy_callback; + + PA_LLIST_FIELDS(pa_time_event); +}; + +struct pa_defer_event { + pa_glib_mainloop *mainloop; + int dead; + + int enabled; + + pa_defer_event_cb_t callback; + void *userdata; + pa_defer_event_destroy_cb_t destroy_callback; + + PA_LLIST_FIELDS(pa_defer_event); +}; + +struct pa_glib_mainloop { + GSource source; + + pa_mainloop_api api; + GMainContext *context; + + PA_LLIST_HEAD(pa_io_event, io_events); + PA_LLIST_HEAD(pa_time_event, time_events); + PA_LLIST_HEAD(pa_defer_event, defer_events); + + int n_enabled_defer_events, n_enabled_time_events; + int io_events_please_scan, time_events_please_scan, defer_events_please_scan; + + pa_time_event *cached_next_time_event; +}; + +static void cleanup_io_events(pa_glib_mainloop *g, int force) { + pa_io_event *e; + + e = g->io_events; + while (e) { + pa_io_event *n = e->next; + + if (!force && g->io_events_please_scan <= 0) + break; + + if (force || e->dead) { + PA_LLIST_REMOVE(pa_io_event, g->io_events, e); + + if (e->dead) { + g_assert(g->io_events_please_scan > 0); + g->io_events_please_scan--; + } + + if (e->poll_fd_added) + g_source_remove_poll(&g->source, &e->poll_fd); + + if (e->destroy_callback) + e->destroy_callback(&g->api, e, e->userdata); + + pa_xfree(e); + } + + e = n; + } + + g_assert(g->io_events_please_scan == 0); +} + +static void cleanup_time_events(pa_glib_mainloop *g, int force) { + pa_time_event *e; + + e = g->time_events; + while (e) { + pa_time_event *n = e->next; + + if (!force && g->time_events_please_scan <= 0) + break; + + if (force || e->dead) { + PA_LLIST_REMOVE(pa_time_event, g->time_events, e); + + if (e->dead) { + g_assert(g->time_events_please_scan > 0); + g->time_events_please_scan--; + } + + if (!e->dead && e->enabled) { + g_assert(g->n_enabled_time_events > 0); + g->n_enabled_time_events--; + } + + if (e->destroy_callback) + e->destroy_callback(&g->api, e, e->userdata); + + pa_xfree(e); + } + + e = n; + } + + g_assert(g->time_events_please_scan == 0); +} + +static void cleanup_defer_events(pa_glib_mainloop *g, int force) { + pa_defer_event *e; + + e = g->defer_events; + while (e) { + pa_defer_event *n = e->next; + + if (!force && g->defer_events_please_scan <= 0) + break; + + if (force || e->dead) { + PA_LLIST_REMOVE(pa_defer_event, g->defer_events, e); + + if (e->dead) { + g_assert(g->defer_events_please_scan > 0); + g->defer_events_please_scan--; + } + + if (!e->dead && e->enabled) { + g_assert(g->n_enabled_defer_events > 0); + g->n_enabled_defer_events--; + } + + if (e->destroy_callback) + e->destroy_callback(&g->api, e, e->userdata); + + pa_xfree(e); + } + + e = n; + } + + g_assert(g->defer_events_please_scan == 0); +} + +static gushort map_flags_to_glib(pa_io_event_flags_t flags) { + return (gushort) + ((flags & PA_IO_EVENT_INPUT ? G_IO_IN : 0) | + (flags & PA_IO_EVENT_OUTPUT ? G_IO_OUT : 0) | + (flags & PA_IO_EVENT_ERROR ? G_IO_ERR : 0) | + (flags & PA_IO_EVENT_HANGUP ? G_IO_HUP : 0)); +} + +static pa_io_event_flags_t map_flags_from_glib(gushort flags) { + return + (flags & G_IO_IN ? PA_IO_EVENT_INPUT : 0) | + (flags & G_IO_OUT ? PA_IO_EVENT_OUTPUT : 0) | + (flags & G_IO_ERR ? PA_IO_EVENT_ERROR : 0) | + (flags & G_IO_HUP ? PA_IO_EVENT_HANGUP : 0); +} + +static pa_io_event* glib_io_new( + pa_mainloop_api*m, + int fd, + pa_io_event_flags_t f, + pa_io_event_cb_t cb, + void *userdata) { + + pa_io_event *e; + pa_glib_mainloop *g; + + g_assert(m); + g_assert(m->userdata); + g_assert(fd >= 0); + g_assert(cb); + + g = m->userdata; + + e = pa_xnew(pa_io_event, 1); + e->mainloop = g; + e->dead = 0; + + e->poll_fd.fd = fd; + e->poll_fd.events = map_flags_to_glib(f); + e->poll_fd.revents = 0; + + e->callback = cb; + e->userdata = userdata; + e->destroy_callback = NULL; + + PA_LLIST_PREPEND(pa_io_event, g->io_events, e); + + g_source_add_poll(&g->source, &e->poll_fd); + e->poll_fd_added = 1; + + return e; +} + +static void glib_io_enable(pa_io_event*e, pa_io_event_flags_t f) { + g_assert(e); + g_assert(!e->dead); + + e->poll_fd.events = map_flags_to_glib(f); +} + +static void glib_io_free(pa_io_event*e) { + g_assert(e); + g_assert(!e->dead); + + e->dead = 1; + e->mainloop->io_events_please_scan++; + + if (e->poll_fd_added) { + g_source_remove_poll(&e->mainloop->source, &e->poll_fd); + e->poll_fd_added = 0; + } +} + +static void glib_io_set_destroy(pa_io_event*e, pa_io_event_destroy_cb_t cb) { + g_assert(e); + g_assert(!e->dead); + + e->destroy_callback = cb; +} + +/* Time sources */ + +static pa_time_event* glib_time_new( + pa_mainloop_api*m, + const struct timeval *tv, + pa_time_event_cb_t cb, + void *userdata) { + + pa_glib_mainloop *g; + pa_time_event *e; + + g_assert(m); + g_assert(m->userdata); + g_assert(cb); + + g = m->userdata; + + e = pa_xnew(pa_time_event, 1); + e->mainloop = g; + e->dead = 0; + + if ((e->enabled = !!tv)) { + e->timeval = *tv; + g->n_enabled_time_events++; + + if (g->cached_next_time_event) { + g_assert(g->cached_next_time_event->enabled); + + if (pa_timeval_cmp(tv, &g->cached_next_time_event->timeval) < 0) + g->cached_next_time_event = e; + } + } + + e->callback = cb; + e->userdata = userdata; + e->destroy_callback = NULL; + + PA_LLIST_PREPEND(pa_time_event, g->time_events, e); + + return e; +} + +static void glib_time_restart(pa_time_event*e, const struct timeval *tv) { + g_assert(e); + g_assert(!e->dead); + + if (e->enabled && !tv) { + g_assert(e->mainloop->n_enabled_time_events > 0); + e->mainloop->n_enabled_time_events--; + } else if (!e->enabled && tv) + e->mainloop->n_enabled_time_events++; + + if ((e->enabled = !!tv)) + e->timeval = *tv; + + if (e->mainloop->cached_next_time_event == e) + e->mainloop->cached_next_time_event = NULL; + + if (e->mainloop->cached_next_time_event && e->enabled) { + g_assert(e->mainloop->cached_next_time_event->enabled); + + if (pa_timeval_cmp(tv, &e->mainloop->cached_next_time_event->timeval) < 0) + e->mainloop->cached_next_time_event = e; + } +} + +static void glib_time_free(pa_time_event *e) { + g_assert(e); + g_assert(!e->dead); + + e->dead = 1; + e->mainloop->time_events_please_scan++; + + if (e->enabled) + e->mainloop->n_enabled_time_events--; + + if (e->mainloop->cached_next_time_event == e) + e->mainloop->cached_next_time_event = NULL; +} + +static void glib_time_set_destroy(pa_time_event *e, pa_time_event_destroy_cb_t cb) { + g_assert(e); + g_assert(!e->dead); + + e->destroy_callback = cb; +} + +/* Deferred sources */ + +static pa_defer_event* glib_defer_new( + pa_mainloop_api*m, + pa_defer_event_cb_t cb, + void *userdata) { + + pa_defer_event *e; + pa_glib_mainloop *g; + + g_assert(m); + g_assert(m->userdata); + g_assert(cb); + + g = m->userdata; + + e = pa_xnew(pa_defer_event, 1); + e->mainloop = g; + e->dead = 0; + + e->enabled = 1; + g->n_enabled_defer_events++; + + e->callback = cb; + e->userdata = userdata; + e->destroy_callback = NULL; + + PA_LLIST_PREPEND(pa_defer_event, g->defer_events, e); + return e; +} + +static void glib_defer_enable(pa_defer_event *e, int b) { + g_assert(e); + g_assert(!e->dead); + + if (e->enabled && !b) { + g_assert(e->mainloop->n_enabled_defer_events > 0); + e->mainloop->n_enabled_defer_events--; + } else if (!e->enabled && b) + e->mainloop->n_enabled_defer_events++; + + e->enabled = b; +} + +static void glib_defer_free(pa_defer_event *e) { + g_assert(e); + g_assert(!e->dead); + + e->dead = 1; + e->mainloop->defer_events_please_scan++; + + if (e->enabled) { + g_assert(e->mainloop->n_enabled_defer_events > 0); + e->mainloop->n_enabled_defer_events--; + } +} + +static void glib_defer_set_destroy(pa_defer_event *e, pa_defer_event_destroy_cb_t cb) { + g_assert(e); + g_assert(!e->dead); + + e->destroy_callback = cb; +} + +/* quit() */ + +static void glib_quit(pa_mainloop_api*a, int retval) { + + g_warning("quit() ignored"); + + /* NOOP */ +} + +static pa_time_event* find_next_time_event(pa_glib_mainloop *g) { + pa_time_event *t, *n = NULL; + g_assert(g); + + if (g->cached_next_time_event) + return g->cached_next_time_event; + + for (t = g->time_events; t; t = t->next) { + + if (t->dead || !t->enabled) + continue; + + if (!n || pa_timeval_cmp(&t->timeval, &n->timeval) < 0) { + n = t; + + /* Shortcut for tv = { 0, 0 } */ + if (n->timeval.tv_sec <= 0) + break; + } + } + + g->cached_next_time_event = n; + return n; +} + +static void scan_dead(pa_glib_mainloop *g) { + g_assert(g); + + if (g->io_events_please_scan) + cleanup_io_events(g, 0); + + if (g->time_events_please_scan) + cleanup_time_events(g, 0); + + if (g->defer_events_please_scan) + cleanup_defer_events(g, 0); +} + +static gboolean prepare_func(GSource *source, gint *timeout) { + pa_glib_mainloop *g = (pa_glib_mainloop*) source; + + g_assert(g); + g_assert(timeout); + + scan_dead(g); + + if (g->n_enabled_defer_events) { + *timeout = 0; + return TRUE; + } else if (g->n_enabled_time_events) { + pa_time_event *t; + gint64 now; + struct timeval tvnow; + pa_usec_t usec; + + t = find_next_time_event(g); + g_assert(t); + + now = g_get_real_time(); + tvnow.tv_sec = now / 1000000; + tvnow.tv_usec = now % 1000000; + + if (pa_timeval_cmp(&t->timeval, &tvnow) <= 0) { + *timeout = 0; + return TRUE; + } + usec = pa_timeval_diff(&t->timeval, &tvnow); + *timeout = (gint) (usec / 1000); + } else + *timeout = -1; + + return FALSE; +} +static gboolean check_func(GSource *source) { + pa_glib_mainloop *g = (pa_glib_mainloop*) source; + pa_io_event *e; + + g_assert(g); + + if (g->n_enabled_defer_events) + return TRUE; + else if (g->n_enabled_time_events) { + pa_time_event *t; + gint64 now; + struct timeval tvnow; + + t = find_next_time_event(g); + g_assert(t); + now = g_get_real_time(); + tvnow.tv_sec = now / 1000000; + tvnow.tv_usec = now % 1000000; + + if (pa_timeval_cmp(&t->timeval, &tvnow) <= 0) + return TRUE; + } + + for (e = g->io_events; e; e = e->next) + if (!e->dead && e->poll_fd.revents != 0) + return TRUE; + + return FALSE; +} + +static gboolean dispatch_func(GSource *source, GSourceFunc callback, gpointer userdata) { + pa_glib_mainloop *g = (pa_glib_mainloop*) source; + pa_io_event *e; + + g_assert(g); + + if (g->n_enabled_defer_events) { + pa_defer_event *d; + + for (d = g->defer_events; d; d = d->next) { + if (d->dead || !d->enabled) + continue; + + break; + } + + g_assert(d); + + d->callback(&g->api, d, d->userdata); + return TRUE; + } + + if (g->n_enabled_time_events) { + gint64 now; + struct timeval tvnow; + pa_time_event *t; + + t = find_next_time_event(g); + g_assert(t); + now = g_get_real_time(); + tvnow.tv_sec = now / 1000000; + tvnow.tv_usec = now % 1000000; + + if (pa_timeval_cmp(&t->timeval, &tvnow) <= 0) { + + /* Disable time event */ + glib_time_restart(t, NULL); + + t->callback(&g->api, t, &t->timeval, t->userdata); + return TRUE; + } + } + + for (e = g->io_events; e; e = e->next) + if (!e->dead && e->poll_fd.revents != 0) { + e->callback(&g->api, e, e->poll_fd.fd, map_flags_from_glib(e->poll_fd.revents), e->userdata); + e->poll_fd.revents = 0; + return TRUE; + } + + return FALSE; +} + +static const pa_mainloop_api vtable = { + .userdata = NULL, + + .io_new = glib_io_new, + .io_enable = glib_io_enable, + .io_free = glib_io_free, + .io_set_destroy = glib_io_set_destroy, + + .time_new = glib_time_new, + .time_restart = glib_time_restart, + .time_free = glib_time_free, + .time_set_destroy = glib_time_set_destroy, + + .defer_new = glib_defer_new, + .defer_enable = glib_defer_enable, + .defer_free = glib_defer_free, + .defer_set_destroy = glib_defer_set_destroy, + + .quit = glib_quit, +}; + +pa_glib_mainloop *pa_glib_mainloop_new(GMainContext *c) { + pa_glib_mainloop *g; + + static GSourceFuncs source_funcs = { + prepare_func, + check_func, + dispatch_func, + NULL, + NULL, + NULL + }; + + g = (pa_glib_mainloop*) g_source_new(&source_funcs, sizeof(pa_glib_mainloop)); + g_main_context_ref(g->context = c ? c : g_main_context_default()); + + g->api = vtable; + g->api.userdata = g; + + PA_LLIST_HEAD_INIT(pa_io_event, g->io_events); + PA_LLIST_HEAD_INIT(pa_time_event, g->time_events); + PA_LLIST_HEAD_INIT(pa_defer_event, g->defer_events); + + g->n_enabled_defer_events = g->n_enabled_time_events = 0; + g->io_events_please_scan = g->time_events_please_scan = g->defer_events_please_scan = 0; + + g->cached_next_time_event = NULL; + + g_source_attach(&g->source, g->context); + g_source_set_can_recurse(&g->source, FALSE); + + return g; +} + +void pa_glib_mainloop_free(pa_glib_mainloop* g) { + g_assert(g); + + cleanup_io_events(g, 1); + cleanup_defer_events(g, 1); + cleanup_time_events(g, 1); + + g_main_context_unref(g->context); + g_source_destroy(&g->source); + g_source_unref(&g->source); +} + +pa_mainloop_api* pa_glib_mainloop_get_api(pa_glib_mainloop *g) { + g_assert(g); + + return &g->api; +} diff --git a/frameworks/innerkitsimpl/pulseaudio/src/pulse/ohos_pa_volume.c b/frameworks/innerkitsimpl/pulseaudio/src/pulse/ohos_pa_volume.c new file mode 100644 index 0000000000000000000000000000000000000000..88feeff22f5835f26af474f8986e1f0cde6ab978 --- /dev/null +++ b/frameworks/innerkitsimpl/pulseaudio/src/pulse/ohos_pa_volume.c @@ -0,0 +1,1007 @@ +/*** + This file is part of PulseAudio. + + Copyright 2004-2006 Lennart Poettering + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, see . +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include + +#include +#include +#include +#include + +#include "volume.h" + +int pa_cvolume_equal(const pa_cvolume *a, const pa_cvolume *b) { + int i; + pa_assert(a); + pa_assert(b); + + pa_return_val_if_fail(pa_cvolume_valid(a), 0); + + if (PA_UNLIKELY(a == b)) + return 1; + + pa_return_val_if_fail(pa_cvolume_valid(b), 0); + + if (a->channels != b->channels) + return 0; + + for (i = 0; i < a->channels; i++) + if (a->values[i] != b->values[i]) + return 0; + + return 1; +} + +pa_cvolume* pa_cvolume_init(pa_cvolume *a) { + unsigned c; + + pa_assert(a); + + a->channels = 0; + + for (c = 0; c < PA_CHANNELS_MAX; c++) + a->values[c] = PA_VOLUME_INVALID; + + return a; +} + +pa_cvolume* pa_cvolume_set(pa_cvolume *a, unsigned channels, pa_volume_t v) { + int i; + + pa_assert(a); + pa_assert(pa_channels_valid(channels)); + + a->channels = (uint8_t) channels; + + for (i = 0; i < a->channels; i++) + /* Clamp in case there is stale data that exceeds the current + * PA_VOLUME_MAX */ + a->values[i] = PA_CLAMP_VOLUME(v); + + return a; +} + +pa_volume_t pa_cvolume_avg(const pa_cvolume *a) { + uint64_t sum = 0; + unsigned c; + + pa_assert(a); + pa_return_val_if_fail(pa_cvolume_valid(a), PA_VOLUME_MUTED); + + for (c = 0; c < a->channels; c++) + sum += a->values[c]; + + sum /= a->channels; + + return (pa_volume_t) sum; +} + +pa_volume_t pa_cvolume_avg_mask(const pa_cvolume *a, const pa_channel_map *cm, pa_channel_position_mask_t mask) { + uint64_t sum = 0; + unsigned c, n; + + pa_assert(a); + + if (!cm) + return pa_cvolume_avg(a); + + pa_return_val_if_fail(pa_cvolume_compatible_with_channel_map(a, cm), PA_VOLUME_MUTED); + + for (c = n = 0; c < a->channels; c++) { + + if (!(PA_CHANNEL_POSITION_MASK(cm->map[c]) & mask)) + continue; + + sum += a->values[c]; + n ++; + } + + if (n > 0) + sum /= n; + + return (pa_volume_t) sum; +} + +pa_volume_t pa_cvolume_max(const pa_cvolume *a) { + pa_volume_t m = PA_VOLUME_MUTED; + unsigned c; + + pa_assert(a); + pa_return_val_if_fail(pa_cvolume_valid(a), PA_VOLUME_MUTED); + + for (c = 0; c < a->channels; c++) + if (a->values[c] > m) + m = a->values[c]; + + return m; +} + +pa_volume_t pa_cvolume_min(const pa_cvolume *a) { + pa_volume_t m = PA_VOLUME_MAX; + unsigned c; + + pa_assert(a); + pa_return_val_if_fail(pa_cvolume_valid(a), PA_VOLUME_MUTED); + + for (c = 0; c < a->channels; c++) + if (a->values[c] < m) + m = a->values[c]; + + return m; +} + +pa_volume_t pa_cvolume_max_mask(const pa_cvolume *a, const pa_channel_map *cm, pa_channel_position_mask_t mask) { + pa_volume_t m = PA_VOLUME_MUTED; + unsigned c; + + pa_assert(a); + + if (!cm) + return pa_cvolume_max(a); + + pa_return_val_if_fail(pa_cvolume_compatible_with_channel_map(a, cm), PA_VOLUME_MUTED); + + for (c = 0; c < a->channels; c++) { + + if (!(PA_CHANNEL_POSITION_MASK(cm->map[c]) & mask)) + continue; + + if (a->values[c] > m) + m = a->values[c]; + } + + return m; +} + +pa_volume_t pa_cvolume_min_mask(const pa_cvolume *a, const pa_channel_map *cm, pa_channel_position_mask_t mask) { + pa_volume_t m = PA_VOLUME_MAX; + unsigned c; + + pa_assert(a); + + if (!cm) + return pa_cvolume_min(a); + + pa_return_val_if_fail(pa_cvolume_compatible_with_channel_map(a, cm), PA_VOLUME_MUTED); + + for (c = 0; c < a->channels; c++) { + + if (!(PA_CHANNEL_POSITION_MASK(cm->map[c]) & mask)) + continue; + + if (a->values[c] < m) + m = a->values[c]; + } + + return m; +} + +pa_volume_t pa_sw_volume_multiply(pa_volume_t a, pa_volume_t b) { + uint64_t result; + + pa_return_val_if_fail(PA_VOLUME_IS_VALID(a), PA_VOLUME_INVALID); + pa_return_val_if_fail(PA_VOLUME_IS_VALID(b), PA_VOLUME_INVALID); + + /* cbrt((a/PA_VOLUME_NORM)^3*(b/PA_VOLUME_NORM)^3)*PA_VOLUME_NORM = a*b/PA_VOLUME_NORM */ + + result = ((uint64_t) a * (uint64_t) b + (uint64_t) PA_VOLUME_NORM / 2ULL) / (uint64_t) PA_VOLUME_NORM; + + if (result > (uint64_t)PA_VOLUME_MAX) + pa_log_warn("pa_sw_volume_multiply: Volume exceeds maximum allowed value and will be clipped. Please check your volume settings."); + + return (pa_volume_t) PA_CLAMP_VOLUME(result); +} + +pa_volume_t pa_sw_volume_divide(pa_volume_t a, pa_volume_t b) { + uint64_t result; + + pa_return_val_if_fail(PA_VOLUME_IS_VALID(a), PA_VOLUME_INVALID); + pa_return_val_if_fail(PA_VOLUME_IS_VALID(b), PA_VOLUME_INVALID); + + if (b <= PA_VOLUME_MUTED) + return 0; + + result = ((uint64_t) a * (uint64_t) PA_VOLUME_NORM + (uint64_t) b / 2ULL) / (uint64_t) b; + + if (result > (uint64_t)PA_VOLUME_MAX) + pa_log_warn("pa_sw_volume_divide: Volume exceeds maximum allowed value and will be clipped. Please check your volume settings."); + + return (pa_volume_t) PA_CLAMP_VOLUME(result); +} + +/* Amplitude, not power */ +static double linear_to_dB(double v) { + return 20.0 * log10(v); +} + +static double dB_to_linear(double v) { + return pow(10.0, v / 20.0); +} + +pa_volume_t pa_sw_volume_from_dB(double dB) { +#ifdef __MUSL__ + if (dB == -INFINITY || dB <= PA_DECIBEL_MININFTY) + return PA_VOLUME_MUTED; +#else + if (isinf(dB) < 0 || dB <= PA_DECIBEL_MININFTY) + return PA_VOLUME_MUTED; +#endif // __MUSL__ + + return pa_sw_volume_from_linear(dB_to_linear(dB)); +} + +double pa_sw_volume_to_dB(pa_volume_t v) { + + pa_return_val_if_fail(PA_VOLUME_IS_VALID(v), PA_DECIBEL_MININFTY); + + if (v <= PA_VOLUME_MUTED) + return PA_DECIBEL_MININFTY; + + return linear_to_dB(pa_sw_volume_to_linear(v)); +} + +pa_volume_t pa_sw_volume_from_linear(double v) { + + if (v <= 0.0) + return PA_VOLUME_MUTED; + + /* + * We use a cubic mapping here, as suggested and discussed here: + * + * http://www.robotplanet.dk/audio/audio_gui_design/ + * http://lists.linuxaudio.org/pipermail/linux-audio-dev/2009-May/thread.html#23151 + * + * We make sure that the conversion to linear and back yields the + * same volume value! That's why we need the lround() below! + */ + + return (pa_volume_t) PA_CLAMP_VOLUME((uint64_t) lround(cbrt(v) * PA_VOLUME_NORM)); +} + +double pa_sw_volume_to_linear(pa_volume_t v) { + double f; + + pa_return_val_if_fail(PA_VOLUME_IS_VALID(v), 0.0); + + if (v <= PA_VOLUME_MUTED) + return 0.0; + + if (v == PA_VOLUME_NORM) + return 1.0; + + f = ((double) v / PA_VOLUME_NORM); + + return f*f*f; +} + +char *pa_cvolume_snprint(char *s, size_t l, const pa_cvolume *c) { + unsigned channel; + bool first = true; + char *e; + + pa_assert(s); + pa_assert(l > 0); + pa_assert(c); + + pa_init_i18n(); + + if (!pa_cvolume_valid(c)) { + pa_snprintf(s, l, _("(invalid)")); + return s; + } + + *(e = s) = 0; + + for (channel = 0; channel < c->channels && l > 1; channel++) { + l -= pa_snprintf(e, l, "%s%u: %3u%%", + first ? "" : " ", + channel, + (unsigned)(((uint64_t)c->values[channel] * 100 + (uint64_t)PA_VOLUME_NORM / 2) / (uint64_t)PA_VOLUME_NORM)); + + e = strchr(e, 0); + first = false; + } + + return s; +} + +char *pa_volume_snprint(char *s, size_t l, pa_volume_t v) { + pa_assert(s); + pa_assert(l > 0); + + pa_init_i18n(); + + if (!PA_VOLUME_IS_VALID(v)) { + pa_snprintf(s, l, _("(invalid)")); + return s; + } + + pa_snprintf(s, l, "%3u%%", (unsigned)(((uint64_t)v * 100 + (uint64_t)PA_VOLUME_NORM / 2) / (uint64_t)PA_VOLUME_NORM)); + return s; +} + +char *pa_sw_cvolume_snprint_dB(char *s, size_t l, const pa_cvolume *c) { + unsigned channel; + bool first = true; + char *e; + + pa_assert(s); + pa_assert(l > 0); + pa_assert(c); + + pa_init_i18n(); + + if (!pa_cvolume_valid(c)) { + pa_snprintf(s, l, _("(invalid)")); + return s; + } + + *(e = s) = 0; + + for (channel = 0; channel < c->channels && l > 1; channel++) { + double f = pa_sw_volume_to_dB(c->values[channel]); + +#ifdef __MUSL__ + l -= pa_snprintf(e, l, "%s%u: %0.2f dB", + first ? "" : " ", + channel, + f == -INFINITY || f <= PA_DECIBEL_MININFTY ? -INFINITY : f); +#else + l -= pa_snprintf(e, l, "%s%u: %0.2f dB", + first ? "" : " ", + channel, + isinf(f) < 0 || f <= PA_DECIBEL_MININFTY ? -INFINITY : f); +#endif // __MUSL__ + + e = strchr(e, 0); + first = false; + } + + return s; +} + +char *pa_cvolume_snprint_verbose(char *s, size_t l, const pa_cvolume *c, const pa_channel_map *map, int print_dB) { + char *current = s; + bool first = true; + + pa_assert(s); + pa_assert(l > 0); + pa_assert(c); + + pa_init_i18n(); + + if (!pa_cvolume_valid(c)) { + pa_snprintf(s, l, _("(invalid)")); + return s; + } + + pa_assert(!map || (map->channels == c->channels)); + pa_assert(!map || pa_channel_map_valid(map)); + + current[0] = 0; + + for (unsigned channel = 0; channel < c->channels && l > 1; channel++) { + char channel_position[32]; + size_t bytes_printed; + char buf[PA_VOLUME_SNPRINT_VERBOSE_MAX]; + + if (map) + pa_snprintf(channel_position, sizeof(channel_position), "%s", pa_channel_position_to_string(map->map[channel])); + else + pa_snprintf(channel_position, sizeof(channel_position), "%u", channel); + + bytes_printed = pa_snprintf(current, l, "%s%s: %s", + first ? "" : ", ", + channel_position, + pa_volume_snprint_verbose(buf, sizeof(buf), c->values[channel], print_dB)); + l -= bytes_printed; + current += bytes_printed; + first = false; + } + + return s; +} + +char *pa_sw_volume_snprint_dB(char *s, size_t l, pa_volume_t v) { + double f; + + pa_assert(s); + pa_assert(l > 0); + + pa_init_i18n(); + + if (!PA_VOLUME_IS_VALID(v)) { + pa_snprintf(s, l, _("(invalid)")); + return s; + } + + f = pa_sw_volume_to_dB(v); +#ifdef __MUSL__ + pa_snprintf(s, l, "%0.2f dB", f == -INFINITY || f <= PA_DECIBEL_MININFTY ? -INFINITY : f); +#else + pa_snprintf(s, l, "%0.2f dB", isinf(f) < 0 || f <= PA_DECIBEL_MININFTY ? -INFINITY : f); +#endif // __MUSL__ + + return s; +} + +char *pa_volume_snprint_verbose(char *s, size_t l, pa_volume_t v, int print_dB) { + char dB[PA_SW_VOLUME_SNPRINT_DB_MAX]; + + pa_assert(s); + pa_assert(l > 0); + + pa_init_i18n(); + + if (!PA_VOLUME_IS_VALID(v)) { + pa_snprintf(s, l, _("(invalid)")); + return s; + } + + pa_snprintf(s, l, "%" PRIu32 " / %3u%%%s%s", + v, + (unsigned)(((uint64_t)v * 100 + (uint64_t)PA_VOLUME_NORM / 2) / (uint64_t)PA_VOLUME_NORM), + print_dB ? " / " : "", + print_dB ? pa_sw_volume_snprint_dB(dB, sizeof(dB), v) : ""); + + return s; +} + +int pa_cvolume_channels_equal_to(const pa_cvolume *a, pa_volume_t v) { + unsigned c; + pa_assert(a); + + pa_return_val_if_fail(pa_cvolume_valid(a), 0); + pa_return_val_if_fail(PA_VOLUME_IS_VALID(v), 0); + + for (c = 0; c < a->channels; c++) + if (a->values[c] != v) + return 0; + + return 1; +} + +pa_cvolume *pa_sw_cvolume_multiply(pa_cvolume *dest, const pa_cvolume *a, const pa_cvolume *b) { + unsigned i; + + pa_assert(dest); + pa_assert(a); + pa_assert(b); + + pa_return_val_if_fail(pa_cvolume_valid(a), NULL); + pa_return_val_if_fail(pa_cvolume_valid(b), NULL); + + dest->channels = PA_MIN(a->channels, b->channels); + + for (i = 0; i < dest->channels; i++) + dest->values[i] = pa_sw_volume_multiply(a->values[i], b->values[i]); + + return dest; +} + +pa_cvolume *pa_sw_cvolume_multiply_scalar(pa_cvolume *dest, const pa_cvolume *a, pa_volume_t b) { + unsigned i; + + pa_assert(dest); + pa_assert(a); + + pa_return_val_if_fail(pa_cvolume_valid(a), NULL); + pa_return_val_if_fail(PA_VOLUME_IS_VALID(b), NULL); + + for (i = 0; i < a->channels; i++) + dest->values[i] = pa_sw_volume_multiply(a->values[i], b); + + dest->channels = (uint8_t) i; + + return dest; +} + +pa_cvolume *pa_sw_cvolume_divide(pa_cvolume *dest, const pa_cvolume *a, const pa_cvolume *b) { + unsigned i; + + pa_assert(dest); + pa_assert(a); + pa_assert(b); + + pa_return_val_if_fail(pa_cvolume_valid(a), NULL); + pa_return_val_if_fail(pa_cvolume_valid(b), NULL); + + dest->channels = PA_MIN(a->channels, b->channels); + + for (i = 0; i < dest->channels; i++) + dest->values[i] = pa_sw_volume_divide(a->values[i], b->values[i]); + + return dest; +} + +pa_cvolume *pa_sw_cvolume_divide_scalar(pa_cvolume *dest, const pa_cvolume *a, pa_volume_t b) { + unsigned i; + + pa_assert(dest); + pa_assert(a); + + pa_return_val_if_fail(pa_cvolume_valid(a), NULL); + pa_return_val_if_fail(PA_VOLUME_IS_VALID(b), NULL); + + for (i = 0; i < a->channels; i++) + dest->values[i] = pa_sw_volume_divide(a->values[i], b); + + dest->channels = (uint8_t) i; + + return dest; +} + +int pa_cvolume_valid(const pa_cvolume *v) { + unsigned c; + + pa_assert(v); + + if (!pa_channels_valid(v->channels)) + return 0; + + for (c = 0; c < v->channels; c++) + if (!PA_VOLUME_IS_VALID(v->values[c])) + return 0; + + return 1; +} + +static bool on_left(pa_channel_position_t p) { + return !!(PA_CHANNEL_POSITION_MASK(p) & PA_CHANNEL_POSITION_MASK_LEFT); +} + +static bool on_right(pa_channel_position_t p) { + return !!(PA_CHANNEL_POSITION_MASK(p) & PA_CHANNEL_POSITION_MASK_RIGHT); +} + +static bool on_center(pa_channel_position_t p) { + return !!(PA_CHANNEL_POSITION_MASK(p) & PA_CHANNEL_POSITION_MASK_CENTER); +} + +static bool on_hfe(pa_channel_position_t p) { + return !!(PA_CHANNEL_POSITION_MASK(p) & PA_CHANNEL_POSITION_MASK_HFE); +} + +static bool on_lfe(pa_channel_position_t p) { + return !!(PA_CHANNEL_POSITION_MASK(p) & PA_CHANNEL_POSITION_MASK_LFE); +} + +static bool on_front(pa_channel_position_t p) { + return !!(PA_CHANNEL_POSITION_MASK(p) & PA_CHANNEL_POSITION_MASK_FRONT); +} + +static bool on_rear(pa_channel_position_t p) { + return !!(PA_CHANNEL_POSITION_MASK(p) & PA_CHANNEL_POSITION_MASK_REAR); +} + +pa_cvolume *pa_cvolume_remap(pa_cvolume *v, const pa_channel_map *from, const pa_channel_map *to) { + int a, b; + pa_cvolume result; + + pa_assert(v); + pa_assert(from); + pa_assert(to); + + pa_return_val_if_fail(pa_channel_map_valid(to), NULL); + pa_return_val_if_fail(pa_cvolume_compatible_with_channel_map(v, from), NULL); + + if (pa_channel_map_equal(from, to)) + return v; + + result.channels = to->channels; + + for (b = 0; b < to->channels; b++) { + pa_volume_t k = 0; + int n = 0; + + for (a = 0; a < from->channels; a++) + if (from->map[a] == to->map[b]) { + k += v->values[a]; + n ++; + } + + if (n <= 0) { + for (a = 0; a < from->channels; a++) + if ((on_left(from->map[a]) && on_left(to->map[b])) || + (on_right(from->map[a]) && on_right(to->map[b])) || + (on_center(from->map[a]) && on_center(to->map[b])) || + (on_lfe(from->map[a]) && on_lfe(to->map[b]))) { + + k += v->values[a]; + n ++; + } + } + + if (n <= 0) + k = pa_cvolume_avg(v); + else + k /= n; + + result.values[b] = k; + } + + *v = result; + return v; +} + +int pa_cvolume_compatible(const pa_cvolume *v, const pa_sample_spec *ss) { + + pa_assert(v); + pa_assert(ss); + + pa_return_val_if_fail(pa_cvolume_valid(v), 0); + pa_return_val_if_fail(pa_sample_spec_valid(ss), 0); + + return v->channels == ss->channels; +} + +int pa_cvolume_compatible_with_channel_map(const pa_cvolume *v, const pa_channel_map *cm) { + pa_assert(v); + pa_assert(cm); + + pa_return_val_if_fail(pa_cvolume_valid(v), 0); + pa_return_val_if_fail(pa_channel_map_valid(cm), 0); + + return v->channels == cm->channels; +} + +/* + * Returns the average volume of l and r, where l and r are two disjoint sets of channels + * (e g left and right, or front and rear). + */ +static void get_avg(const pa_channel_map *map, const pa_cvolume *v, pa_volume_t *l, pa_volume_t *r, + bool (*on_l)(pa_channel_position_t), bool (*on_r)(pa_channel_position_t)) { + int c; + pa_volume_t left = 0, right = 0; + unsigned n_left = 0, n_right = 0; + + pa_assert(v); + pa_assert(map); + pa_assert(map->channels == v->channels); + pa_assert(l); + pa_assert(r); + + for (c = 0; c < map->channels; c++) { + if (on_l(map->map[c])) { + left += v->values[c]; + n_left++; + } else if (on_r(map->map[c])) { + right += v->values[c]; + n_right++; + } + } + + if (n_left <= 0) + *l = PA_VOLUME_NORM; + else + *l = left / n_left; + + if (n_right <= 0) + *r = PA_VOLUME_NORM; + else + *r = right / n_right; +} + +float pa_cvolume_get_balance(const pa_cvolume *v, const pa_channel_map *map) { + pa_volume_t left, right; + + pa_assert(v); + pa_assert(map); + + pa_return_val_if_fail(pa_cvolume_compatible_with_channel_map(v, map), 0.0f); + + if (!pa_channel_map_can_balance(map)) + return 0.0f; + + get_avg(map, v, &left, &right, on_left, on_right); + + if (left == right) + return 0.0f; + + /* 1.0, 0.0 => -1.0 + 0.0, 1.0 => 1.0 + 0.0, 0.0 => 0.0 + 0.5, 0.5 => 0.0 + 1.0, 0.5 => -0.5 + 1.0, 0.25 => -0.75 + 0.75, 0.25 => -0.66 + 0.5, 0.25 => -0.5 */ + + if (left > right) + return -1.0f + ((float) right / (float) left); + else + return 1.0f - ((float) left / (float) right); +} + +static pa_cvolume* set_balance(pa_cvolume *v, const pa_channel_map *map, float new_balance, + bool (*on_l)(pa_channel_position_t), bool (*on_r)(pa_channel_position_t)) { + + pa_volume_t left, nleft, right, nright, m; + unsigned c; + + get_avg(map, v, &left, &right, on_l, on_r); + + m = PA_MAX(left, right); + + if (new_balance <= 0) { + nright = (new_balance + 1.0f) * m; + nleft = m; + } else { + nleft = (1.0f - new_balance) * m; + nright = m; + } + + for (c = 0; c < map->channels; c++) { + if (on_l(map->map[c])) { + if (left == 0) + v->values[c] = nleft; + else + v->values[c] = (pa_volume_t) PA_CLAMP_VOLUME(((uint64_t) v->values[c] * (uint64_t) nleft) / (uint64_t) left); + } else if (on_r(map->map[c])) { + if (right == 0) + v->values[c] = nright; + else + v->values[c] = (pa_volume_t) PA_CLAMP_VOLUME(((uint64_t) v->values[c] * (uint64_t) nright) / (uint64_t) right); + } + } + + return v; +} + + +pa_cvolume* pa_cvolume_set_balance(pa_cvolume *v, const pa_channel_map *map, float new_balance) { + pa_assert(map); + pa_assert(v); + + pa_return_val_if_fail(pa_cvolume_compatible_with_channel_map(v, map), NULL); + pa_return_val_if_fail(new_balance >= -1.0f, NULL); + pa_return_val_if_fail(new_balance <= 1.0f, NULL); + + if (!pa_channel_map_can_balance(map)) + return v; + + return set_balance(v, map, new_balance, on_left, on_right); +} + +pa_cvolume* pa_cvolume_scale(pa_cvolume *v, pa_volume_t max) { + unsigned c; + pa_volume_t t = 0; + + pa_assert(v); + + pa_return_val_if_fail(pa_cvolume_valid(v), NULL); + pa_return_val_if_fail(PA_VOLUME_IS_VALID(max), NULL); + + t = pa_cvolume_max(v); + + if (t <= PA_VOLUME_MUTED) + return pa_cvolume_set(v, v->channels, max); + + for (c = 0; c < v->channels; c++) + v->values[c] = (pa_volume_t) PA_CLAMP_VOLUME(((uint64_t) v->values[c] * (uint64_t) max) / (uint64_t) t); + + return v; +} + +pa_cvolume* pa_cvolume_scale_mask(pa_cvolume *v, pa_volume_t max, const pa_channel_map *cm, pa_channel_position_mask_t mask) { + unsigned c; + pa_volume_t t = 0; + + pa_assert(v); + + pa_return_val_if_fail(PA_VOLUME_IS_VALID(max), NULL); + + if (!cm) + return pa_cvolume_scale(v, max); + + pa_return_val_if_fail(pa_cvolume_compatible_with_channel_map(v, cm), NULL); + + t = pa_cvolume_max_mask(v, cm, mask); + + if (t <= PA_VOLUME_MUTED) + return pa_cvolume_set(v, v->channels, max); + + for (c = 0; c < v->channels; c++) + v->values[c] = (pa_volume_t) PA_CLAMP_VOLUME(((uint64_t) v->values[c] * (uint64_t) max) / (uint64_t) t); + + return v; +} + +float pa_cvolume_get_fade(const pa_cvolume *v, const pa_channel_map *map) { + pa_volume_t rear, front; + + pa_assert(v); + pa_assert(map); + + pa_return_val_if_fail(pa_cvolume_compatible_with_channel_map(v, map), 0.0f); + + if (!pa_channel_map_can_fade(map)) + return 0.0f; + + get_avg(map, v, &rear, &front, on_rear, on_front); + + if (front == rear) + return 0.0f; + + if (rear > front) + return -1.0f + ((float) front / (float) rear); + else + return 1.0f - ((float) rear / (float) front); +} + +pa_cvolume* pa_cvolume_set_fade(pa_cvolume *v, const pa_channel_map *map, float new_fade) { + pa_assert(map); + pa_assert(v); + + pa_return_val_if_fail(pa_cvolume_compatible_with_channel_map(v, map), NULL); + pa_return_val_if_fail(new_fade >= -1.0f, NULL); + pa_return_val_if_fail(new_fade <= 1.0f, NULL); + + if (!pa_channel_map_can_fade(map)) + return v; + + return set_balance(v, map, new_fade, on_rear, on_front); +} + +float pa_cvolume_get_lfe_balance(const pa_cvolume *v, const pa_channel_map *map) { + pa_volume_t hfe, lfe; + + pa_assert(v); + pa_assert(map); + + pa_return_val_if_fail(pa_cvolume_compatible_with_channel_map(v, map), 0.0f); + + if (!pa_channel_map_can_lfe_balance(map)) + return 0.0f; + + get_avg(map, v, &hfe, &lfe, on_hfe, on_lfe); + + if (hfe == lfe) + return 0.0f; + + if (hfe > lfe) + return -1.0f + ((float) lfe / (float) hfe); + else + return 1.0f - ((float) hfe / (float) lfe); +} + +pa_cvolume* pa_cvolume_set_lfe_balance(pa_cvolume *v, const pa_channel_map *map, float new_balance) { + pa_assert(map); + pa_assert(v); + + pa_return_val_if_fail(pa_cvolume_compatible_with_channel_map(v, map), NULL); + pa_return_val_if_fail(new_balance >= -1.0f, NULL); + pa_return_val_if_fail(new_balance <= 1.0f, NULL); + + if (!pa_channel_map_can_lfe_balance(map)) + return v; + + return set_balance(v, map, new_balance, on_hfe, on_lfe); +} + +pa_cvolume* pa_cvolume_set_position( + pa_cvolume *cv, + const pa_channel_map *map, + pa_channel_position_t t, + pa_volume_t v) { + + unsigned c; + bool good = false; + + pa_assert(cv); + pa_assert(map); + + pa_return_val_if_fail(pa_cvolume_compatible_with_channel_map(cv, map), NULL); + pa_return_val_if_fail(t < PA_CHANNEL_POSITION_MAX, NULL); + pa_return_val_if_fail(PA_VOLUME_IS_VALID(v), NULL); + + for (c = 0; c < map->channels; c++) + if (map->map[c] == t) { + cv->values[c] = v; + good = true; + } + + return good ? cv : NULL; +} + +pa_volume_t pa_cvolume_get_position( + const pa_cvolume *cv, + const pa_channel_map *map, + pa_channel_position_t t) { + + unsigned c; + pa_volume_t v = PA_VOLUME_MUTED; + + pa_assert(cv); + pa_assert(map); + + pa_return_val_if_fail(pa_cvolume_compatible_with_channel_map(cv, map), PA_VOLUME_MUTED); + pa_return_val_if_fail(t < PA_CHANNEL_POSITION_MAX, PA_VOLUME_MUTED); + + for (c = 0; c < map->channels; c++) + if (map->map[c] == t) + if (cv->values[c] > v) + v = cv->values[c]; + + return v; +} + +pa_cvolume* pa_cvolume_merge(pa_cvolume *dest, const pa_cvolume *a, const pa_cvolume *b) { + unsigned i; + + pa_assert(dest); + pa_assert(a); + pa_assert(b); + + pa_return_val_if_fail(pa_cvolume_valid(a), NULL); + pa_return_val_if_fail(pa_cvolume_valid(b), NULL); + + dest->channels = PA_MIN(a->channels, b->channels); + + for (i = 0; i < dest->channels; i++) + dest->values[i] = PA_MAX(a->values[i], b->values[i]); + + return dest; +} + +pa_cvolume* pa_cvolume_inc_clamp(pa_cvolume *v, pa_volume_t inc, pa_volume_t limit) { + pa_volume_t m; + + pa_assert(v); + + pa_return_val_if_fail(pa_cvolume_valid(v), NULL); + pa_return_val_if_fail(PA_VOLUME_IS_VALID(inc), NULL); + + m = pa_cvolume_max(v); + + if (m >= limit - inc) + m = limit; + else + m += inc; + + return pa_cvolume_scale(v, m); +} + +pa_cvolume* pa_cvolume_inc(pa_cvolume *v, pa_volume_t inc) { + return pa_cvolume_inc_clamp(v, inc, PA_VOLUME_MAX); +} + +pa_cvolume* pa_cvolume_dec(pa_cvolume *v, pa_volume_t dec) { + pa_volume_t m; + + pa_assert(v); + + pa_return_val_if_fail(pa_cvolume_valid(v), NULL); + pa_return_val_if_fail(PA_VOLUME_IS_VALID(dec), NULL); + + m = pa_cvolume_max(v); + + if (m <= PA_VOLUME_MUTED + dec) + m = PA_VOLUME_MUTED; + else + m -= dec; + + return pa_cvolume_scale(v, m); +} diff --git a/frameworks/innerkitsimpl/pulseaudio/src/pulsecore/BUILD.gn b/frameworks/innerkitsimpl/pulseaudio/src/pulsecore/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..80737bb00d46f6f042d00f12bab1084e5a8ee366 --- /dev/null +++ b/frameworks/innerkitsimpl/pulseaudio/src/pulsecore/BUILD.gn @@ -0,0 +1,182 @@ +# Copyright (C) 2021 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") + +pulseaudio_dir = "//third_party/pulseaudio" +pulseaudio_build_path = "//foundation/multimedia/audio_standard/frameworks/innerkitsimpl/pulseaudio" +libsndfile_build_path = "//foundation/multimedia/audio_standard/frameworks/innerkitsimpl/libsnd" + +config("pulsecore_config") { + visibility = [ ":*" ] + + include_dirs = [ + "$pulseaudio_dir", + "$pulseaudio_dir/src", + "$pulseaudio_build_path/include", + "$pulseaudio_build_path/src", + "//third_party/glib/glib", + "//third_party/glib", + "$libsndfile_build_path/include", + ] + + cflags = [ + "-Wall", + "-Werror", + "-Wno-implicit-function-declaration", + "-Wno-unused-function", + "-Wno-uninitialized", + "-DHAVE_CONFIG_H", + "-D_GNU_SOURCE", + "-D__INCLUDED_FROM_PULSE_AUDIO", + ] +} + +ohos_source_set("pulsecore_sources") { + sources = [ + "$pulseaudio_build_path/src/pulsecore/ltdl-stub.c", + "$pulseaudio_dir/src/pulsecore/asyncmsgq.c", + "$pulseaudio_dir/src/pulsecore/asyncq.c", + "$pulseaudio_dir/src/pulsecore/auth-cookie.c", + "$pulseaudio_dir/src/pulsecore/card.c", + "$pulseaudio_dir/src/pulsecore/cli-command.c", + "$pulseaudio_dir/src/pulsecore/cli-text.c", + "$pulseaudio_dir/src/pulsecore/client.c", + "$pulseaudio_dir/src/pulsecore/core-scache.c", + "$pulseaudio_dir/src/pulsecore/core-subscribe.c", + "$pulseaudio_dir/src/pulsecore/core.c", + #"$pulseaudio_dir/src/pulsecore/cpu.c", + "$pulseaudio_dir/src/pulsecore/cpu-arm.c", + #"$pulseaudio_dir/src/pulsecore/cpu-orc.c", + #"$pulseaudio_dir/src/pulsecore/cpu-x86.c", + "$pulseaudio_dir/src/pulsecore/device-port.c", + "$pulseaudio_dir/src/pulsecore/database.c", + "$pulseaudio_dir/src/pulsecore/database-simple.c", + "$pulseaudio_dir/src/pulsecore/ffmpeg/resample2.c", + "$pulseaudio_dir/src/pulsecore/filter/biquad.c", + "$pulseaudio_dir/src/pulsecore/filter/crossover.c", + "$pulseaudio_dir/src/pulsecore/filter/lfe-filter.c", + "$pulseaudio_dir/src/pulsecore/hook-list.c", + "$pulseaudio_dir/src/pulsecore/ltdl-helper.c", + "$pulseaudio_dir/src/pulsecore/message-handler.c", + "$pulseaudio_dir/src/pulsecore/mix.c", + "$pulseaudio_dir/src/pulsecore/modargs.c", + "$pulseaudio_dir/src/pulsecore/modinfo.c", + "$pulseaudio_dir/src/pulsecore/module.c", + "$pulseaudio_dir/src/pulsecore/msgobject.c", + "$pulseaudio_dir/src/pulsecore/namereg.c", + "$pulseaudio_dir/src/pulsecore/object.c", + "$pulseaudio_dir/src/pulsecore/play-memblockq.c", + "$pulseaudio_dir/src/pulsecore/play-memchunk.c", + "$pulseaudio_dir/src/pulsecore/remap.c", + "$pulseaudio_dir/src/pulsecore/resampler.c", + "$pulseaudio_dir/src/pulsecore/resampler/ffmpeg.c", + "$pulseaudio_dir/src/pulsecore/resampler/peaks.c", + "$pulseaudio_dir/src/pulsecore/resampler/trivial.c", + "$pulseaudio_dir/src/pulsecore/rtpoll.c", + "$pulseaudio_dir/src/pulsecore/sconv-s16be.c", + "$pulseaudio_dir/src/pulsecore/sconv-s16le.c", + "$pulseaudio_dir/src/pulsecore/sconv.c", + "$pulseaudio_dir/src/pulsecore/shared.c", + "$pulseaudio_dir/src/pulsecore/sink.c", + "$pulseaudio_dir/src/pulsecore/sink-input.c", + "$pulseaudio_dir/src/pulsecore/sioman.c", + "$pulseaudio_dir/src/pulsecore/sound-file-stream.c", + "$pulseaudio_dir/src/pulsecore/sound-file.c", + "$pulseaudio_dir/src/pulsecore/source.c", + "$pulseaudio_dir/src/pulsecore/source-output.c", + "$pulseaudio_dir/src/pulsecore/start-child.c", + "$pulseaudio_dir/src/pulsecore/stream-util.c", + "$pulseaudio_dir/src/pulsecore/svolume_arm.c", + "$pulseaudio_dir/src/pulsecore/svolume_c.c", + "$pulseaudio_dir/src/pulsecore/svolume_mmx.c", + "$pulseaudio_dir/src/pulsecore/svolume_sse.c", + "$pulseaudio_dir/src/pulsecore/thread-mq.c", + ] + + configs = [ ":pulsecore_config" ] +} + +ohos_shared_library("pulsecore") { + deps = [ + ":pulsecore_sources", + "$pulseaudio_build_path/src:pulsecommon", + "$libsndfile_build_path:sndfile", + ] + + subsystem_name = "multimedia" + part_name = "multimedia_audio_standard" +} + +config("modules_internal_lib_config") { + visibility = [ ":*" ] + + include_dirs = [ + "$pulseaudio_build_path/include", + "$pulseaudio_dir/src", + "$pulseaudio_dir", + "$pulseaudio_build_path/src", + ] + + cflags = [ + "-Wall", + "-Werror", + "-Wno-unused-function", + "-DHAVE_CONFIG_H", + "-D_GNU_SOURCE", + "-D__INCLUDED_FROM_PULSE_AUDIO", + ] +} + +ohos_shared_library("cli") { + sources = [ "$pulseaudio_dir/src/pulsecore/cli.c" ] + + configs = [ ":modules_internal_lib_config" ] + + deps = [ + "$pulseaudio_build_path/src:pulsecommon", + "$pulseaudio_build_path/src/pulsecore:pulsecore", + ] + + subsystem_name = "multimedia" + part_name = "multimedia_audio_standard" +} + +ohos_shared_library("protocol-cli") { + sources = [ "$pulseaudio_dir/src/pulsecore/protocol-cli.c" ] + + configs = [ ":modules_internal_lib_config" ] + + deps = [ + "$pulseaudio_build_path/src:pulsecommon", + "$pulseaudio_build_path/src/pulsecore:pulsecore", + "$pulseaudio_build_path/src/pulsecore:cli", + ] + + subsystem_name = "multimedia" + part_name = "multimedia_audio_standard" +} + +ohos_shared_library("protocol-native") { + sources = [ "$pulseaudio_dir/src/pulsecore/protocol-native.c" ] + + configs = [ ":modules_internal_lib_config" ] + + deps = [ + "$pulseaudio_build_path/src:pulsecommon", + "$pulseaudio_build_path/src/pulsecore:pulsecore", + ] + + subsystem_name = "multimedia" + part_name = "multimedia_audio_standard" +} diff --git a/frameworks/innerkitsimpl/pulseaudio/src/pulsecore/ltdl-stub.c b/frameworks/innerkitsimpl/pulseaudio/src/pulsecore/ltdl-stub.c new file mode 100644 index 0000000000000000000000000000000000000000..ce239ab9b8ef4a8b822326f58214da872cc3ab67 --- /dev/null +++ b/frameworks/innerkitsimpl/pulseaudio/src/pulsecore/ltdl-stub.c @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2021 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 "config.h" +#include +#include "ltdl.h" +#include + +#define SYSTEM_LIB_PATH "/system/lib/" + +lt_dlhandle lt_dlopenext(const char *filename) { + pa_assert(filename); + return (dlopen(filename, RTLD_NOW)); +} + +void* lt_dlsym(lt_dlhandle handle, const char *symbol) { + pa_assert(handle); + pa_assert(symbol); + + return (dlsym(handle, symbol)); +} + +int lt_dlclose(lt_dlhandle handle) { + pa_assert(handle); + return (dlclose(handle)); +} + +const char *lt_dlerror(void) { + return dlerror(); +} + +const char* lt_dlgetsearchpath() { + const char* path = SYSTEM_LIB_PATH; + return path; +} diff --git a/frameworks/innerkitsimpl/pulseaudio/src/utils/BUILD.gn b/frameworks/innerkitsimpl/pulseaudio/src/utils/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..39a40462bbddff84667a8a5f979779c14a07c201 --- /dev/null +++ b/frameworks/innerkitsimpl/pulseaudio/src/utils/BUILD.gn @@ -0,0 +1,85 @@ +# Copyright (C) 2021 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") + +pulseaudio_dir = "//third_party/pulseaudio" +pulseaudio_build_path = "//foundation/multimedia/audio_standard/frameworks/innerkitsimpl/pulseaudio" +libsndfile_build_path = "//foundation/multimedia/audio_standard/frameworks/innerkitsimpl/libsnd" + +config("pulseutils_config") { + visibility = [ ":*" ] + + include_dirs = [ + "$pulseaudio_dir", + "$pulseaudio_build_path/src", + "$pulseaudio_build_path/include", + "$pulseaudio_dir/src", + "$libsndfile_build_path/include", + ] + + cflags = [ + "-Wall", + "-Werror", + "-Wno-implicit-function-declaration", + "-DHAVE_CONFIG_H", + "-D_GNU_SOURCE", + ] +} + +ohos_executable("pacat") { + + sources = [ "$pulseaudio_dir/src/utils/pacat.c" ] + + configs = [ ":pulseutils_config" ] + + deps = [ + "$pulseaudio_build_path/src/pulse:pulse", + "$pulseaudio_build_path/src:pulsecommon", + "$libsndfile_build_path:sndfile", + ] + + subsystem_name = "multimedia" + part_name = "multimedia_audio_standard" +} + +ohos_executable("pactl") { + sources = [ "$pulseaudio_dir/src/utils/pactl.c" ] + + configs = [ ":pulseutils_config" ] + + deps = [ + "$pulseaudio_build_path/src/pulse:pulse", + "$pulseaudio_build_path/src:pulsecommon", + "$libsndfile_build_path:sndfile", + ] + subsystem_name = "multimedia" + part_name = "multimedia_audio_standard" +} + +ohos_executable("pacmd") { + + install_enable = true + + sources = [ "$pulseaudio_dir/src/utils/pacmd.c" ] + + configs = [ ":pulseutils_config" ] + + deps = [ + "$pulseaudio_build_path/src/pulse:pulse", + "$pulseaudio_build_path/src:pulsecommon", + ] + + subsystem_name = "multimedia" + part_name = "multimedia_audio_standard" +} diff --git a/frameworks/innerkitsimpl/test/BUILD.gn b/frameworks/innerkitsimpl/test/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..5fa4016c5a04bbfc91691b7f6e3fbf3d20805a32 --- /dev/null +++ b/frameworks/innerkitsimpl/test/BUILD.gn @@ -0,0 +1,22 @@ +# Copyright (C) 2021 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. + +group("audio_test") { + testonly = true + + deps = [ + "moduletest/renderer_test:moduletest", + "moduletest/recorder_test:audio_recorder_module_test", + "moduletest/audiopolicy:audio_policy_module_test" + ] +} diff --git a/frameworks/innerkitsimpl/test/moduletest/audiopolicy/1.0/include/audio_policy_test.h b/frameworks/innerkitsimpl/test/moduletest/audiopolicy/1.0/include/audio_policy_test.h new file mode 100644 index 0000000000000000000000000000000000000000..472f942660496fcd4c8f7474354f7281ee2638dc --- /dev/null +++ b/frameworks/innerkitsimpl/test/moduletest/audiopolicy/1.0/include/audio_policy_test.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2021 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 + +namespace OHOS { +namespace AudioStandard { +namespace V1_0 { +using namespace std; +using namespace testing; + +struct PolicyParam { + float volume; + AudioStreamType streamType; + AudioRingerMode ringerMode; + AudioStandard::DeviceType deviceType; + AudioStandard::AudioDeviceDescriptor::DeviceFlag deviceFlag; + AudioStandard::AudioDeviceDescriptor::DeviceRole deviceRole; + bool active; + bool mute; + string key; + string value; +}; + +class AudioPolicyTest : public TestWithParam { +public: + AudioPolicyTest() {} + + virtual ~AudioPolicyTest() {} + + // SetUpTestCase: Called before all test cases + static void SetUpTestCase(void); + // TearDownTestCase: Called after all test case + static void TearDownTestCase(void); + // SetUp: Called before each test cases + void SetUp(void) override; + // TearDown: Called after each test cases + void TearDown(void) override; +}; +} +} +} diff --git a/frameworks/innerkitsimpl/test/moduletest/audiopolicy/1.0/src/audio_policy_test.cpp b/frameworks/innerkitsimpl/test/moduletest/audiopolicy/1.0/src/audio_policy_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f2d5fb8efa3b6e300b7e54f098234e61974dc36e --- /dev/null +++ b/frameworks/innerkitsimpl/test/moduletest/audiopolicy/1.0/src/audio_policy_test.cpp @@ -0,0 +1,493 @@ +/* + * Copyright (C) 2021 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_policy_test.h" +#include "audio_system_manager.h" + +using namespace std; +using namespace OHOS::AudioStandard; +using namespace testing::ext; + +namespace OHOS { +namespace AudioStandard { +namespace V1_0 { +void AudioPolicyTest::SetUpTestCase(void) +{ + ASSERT_NE(nullptr, AudioSystemManager::GetInstance()); +} + +void AudioPolicyTest::TearDownTestCase(void) {} + +void AudioPolicyTest::SetUp(void) {} + +void AudioPolicyTest::TearDown(void) {} + +namespace { +const PolicyParam VOLUME_PARAMS[] = { + { + .volume = 0.5, + .streamType = STREAM_MUSIC + }, + { + .volume = 0.5, + .streamType = STREAM_RING + } +}; + +const PolicyParam MUTE_PARAMS[] = { + { + .streamType = STREAM_MUSIC, + .mute = true + }, + { + .streamType = STREAM_MUSIC, + .mute = false + } +}; + +const PolicyParam STREAM_PARAMS[] = { + { + .streamType = STREAM_MUSIC, + .active = true + }, + { + .streamType = STREAM_RING, + .active = false + } +}; + +const PolicyParam DEVICE_PARAMS[] = { + { + .deviceType = SPEAKER, + .active = true + }, + { + .deviceType = MIC, + .active = true + }, + { + .deviceType = BLUETOOTH_A2DP, + .active = true + }, + { + .deviceType = BLUETOOTH_A2DP, + .active = false + }, + { + .deviceType = BLUETOOTH_SCO, + .active = true + }, + { + .deviceType = BLUETOOTH_SCO, + .active = false + }, + { + .deviceType = MIC, + .active = true + }, + { + .deviceType = BLUETOOTH_SCO, + .active = true + }, + { + .deviceType = SPEAKER, + .active = true + }, + { + .deviceType = BLUETOOTH_A2DP, + .active = true + }, +}; + +const PolicyParam RINGER_MODE_PARAMS[] = { + { + .ringerMode = RINGER_MODE_NORMAL + }, + { + .ringerMode = RINGER_MODE_SILENT + }, + { + .ringerMode = RINGER_MODE_VIBRATE + }, +}; + +const PolicyParam MIC_MUTE_PARAMS[] = { + { + .mute = true + }, + { + .mute = false + } +}; + +const PolicyParam VOLUME_RANGE_PARAMS[] = { + { + .streamType = STREAM_MUSIC + }, + { + .streamType = STREAM_RING + } +}; + +const PolicyParam AUDIO_PARAMS[] = { + { + .key = "sampling_rate", + .value = "8000" + }, + { + .key = "sampling_rate", + .value = "44100" + }, + { + .key = "sampling_rate", + .value = "96000" + } +}; + +const PolicyParam DEVICES_PARAMS[] = { + { + .deviceType = MIC, + .deviceFlag = AudioDeviceDescriptor::DeviceFlag::INPUT_DEVICES_FLAG, + .deviceRole = AudioDeviceDescriptor::DeviceRole::INPUT_DEVICE + }, + { + .deviceType = SPEAKER, + .deviceFlag = AudioDeviceDescriptor::DeviceFlag::OUTPUT_DEVICES_FLAG, + .deviceRole = AudioDeviceDescriptor::DeviceRole::OUTPUT_DEVICE + } +}; +} // namespace + +/* + * Set Volume + * + */ +class AudioPolicySetVolumeTest : public AudioPolicyTest {}; + +HWTEST_P(AudioPolicySetVolumeTest, SetVolume, TestSize.Level1) +{ + PolicyParam params = GetParam(); + + AudioSystemManager::AudioVolumeType volumeType + = static_cast(params.streamType); + float volume = params.volume; + EXPECT_EQ(MEDIA_OK, AudioSystemManager::GetInstance()->SetVolume(volumeType, volume)); +} + +INSTANTIATE_TEST_CASE_P( + SetVolume, + AudioPolicySetVolumeTest, + ValuesIn(VOLUME_PARAMS)); + +/* + * Get Volume + * + */ + +class AudioPolicyGetVolumeTest : public AudioPolicyTest {}; + +HWTEST_P(AudioPolicyGetVolumeTest, GetVolume, TestSize.Level1) +{ + PolicyParam params = GetParam(); + AudioSystemManager::AudioVolumeType volumeType + = static_cast(params.streamType); + float volume = params.volume; + + EXPECT_EQ(MEDIA_OK, AudioSystemManager::GetInstance()->SetVolume(volumeType, volume)); + EXPECT_EQ(volume, AudioSystemManager::GetInstance()->GetVolume(volumeType)); +} + +INSTANTIATE_TEST_CASE_P( + GetVolume, + AudioPolicyGetVolumeTest, + ValuesIn(VOLUME_PARAMS)); + +/* + * Set Mute + * + */ +class AudioPolicySetMuteTest : public AudioPolicyTest {}; + +HWTEST_P(AudioPolicySetMuteTest, SetMute, TestSize.Level1) +{ + PolicyParam params = GetParam(); + AudioSystemManager::AudioVolumeType volumeType + = static_cast(params.streamType); + bool mute = params.mute; + + EXPECT_EQ(MEDIA_OK, AudioSystemManager::GetInstance()->SetMute(volumeType, mute)); +} + + +INSTANTIATE_TEST_CASE_P( + SetMute, + AudioPolicySetMuteTest, + ValuesIn(MUTE_PARAMS)); + +/* + * Is Mute + * + */ +class AudioPolicyGetMuteTest : public AudioPolicyTest {}; + +HWTEST_P(AudioPolicyGetMuteTest, IsStreamMute, TestSize.Level1) +{ + PolicyParam params = GetParam(); + AudioSystemManager::AudioVolumeType volumeType + = static_cast(params.streamType); + bool mute = params.mute; + + EXPECT_EQ(MEDIA_OK, AudioSystemManager::GetInstance()->SetMute(volumeType, mute)); + EXPECT_EQ(mute, AudioSystemManager::GetInstance()->IsStreamMute(volumeType)); +} + +INSTANTIATE_TEST_CASE_P( + IsStreamMute, + AudioPolicyGetMuteTest, + ValuesIn(MUTE_PARAMS)); + +/* + * Is Stream Active + * + */ +class AudioPolicyIsStreamActiveTest : public AudioPolicyTest {}; + +HWTEST_P(AudioPolicyIsStreamActiveTest, IsStreamActive, TestSize.Level1) +{ + PolicyParam params = GetParam(); + AudioSystemManager::AudioVolumeType volumeType + = static_cast(params.streamType); + + // review this code + EXPECT_EQ(params.active, AudioSystemManager::GetInstance()->IsStreamActive(volumeType)); +} + +INSTANTIATE_TEST_CASE_P( + IsStreamActive, + AudioPolicyIsStreamActiveTest, + ValuesIn(STREAM_PARAMS)); + +/* + * Set Device Active + * + */ +class AudioPolicySetDeviceActiveTest : public AudioPolicyTest {}; + +HWTEST_P(AudioPolicySetDeviceActiveTest, SetDeviceActive, TestSize.Level1) +{ + PolicyParam params = GetParam(); + AudioDeviceDescriptor::DeviceType deviceType = static_cast(params.deviceType); + bool active = params.active; + + EXPECT_EQ(MEDIA_OK, AudioSystemManager::GetInstance()->SetDeviceActive(deviceType, active)); +} + +INSTANTIATE_TEST_CASE_P( + SetDeviceActive, + AudioPolicySetDeviceActiveTest, + ValuesIn(DEVICE_PARAMS)); + +/* + * Is Device Active + * + */ + +class AudioPolicyIsDeviceActiveTest : public AudioPolicyTest {}; + +HWTEST_P(AudioPolicyIsDeviceActiveTest, IsDeviceActive, TestSize.Level1) +{ + PolicyParam params = GetParam(); + AudioDeviceDescriptor::DeviceType deviceType = static_cast(params.deviceType); + bool active = params.active; + + EXPECT_EQ(MEDIA_OK, AudioSystemManager::GetInstance()->SetDeviceActive(deviceType, active)); + EXPECT_EQ(active, AudioSystemManager::GetInstance()->IsDeviceActive(deviceType)); +} + + +INSTANTIATE_TEST_CASE_P( + IsDeviceActive, + AudioPolicyIsDeviceActiveTest, + ValuesIn(DEVICE_PARAMS)); + +/* + * Set Ringer Mode + * + */ +class AudioPolicySetRingerModeTest : public AudioPolicyTest {}; + +HWTEST_P(AudioPolicySetRingerModeTest, SetRingerMode, TestSize.Level1) +{ + PolicyParam params = GetParam(); + AudioRingerMode ringerMode = params.ringerMode; + + EXPECT_EQ(MEDIA_OK, AudioSystemManager::GetInstance()->SetRingerMode(ringerMode)); +} + + +INSTANTIATE_TEST_CASE_P( + SetRingerMode, + AudioPolicySetRingerModeTest, + ValuesIn(RINGER_MODE_PARAMS)); + +/* + * Get Ringer Mode + * + */ + +class AudioPolicyGetRingerModeTest : public AudioPolicyTest {}; + +HWTEST_P(AudioPolicyGetRingerModeTest, GetRingerMode, TestSize.Level1) +{ + PolicyParam params = GetParam(); + AudioRingerMode ringerMode = params.ringerMode; + + EXPECT_EQ(MEDIA_OK, AudioSystemManager::GetInstance()->SetRingerMode(ringerMode)); + EXPECT_EQ(ringerMode, AudioSystemManager::GetInstance()->GetRingerMode()); +} + +INSTANTIATE_TEST_CASE_P( + GetRingerMode, + AudioPolicyGetRingerModeTest, + ValuesIn(RINGER_MODE_PARAMS)); + + +/* + * Set microphone mute + * + */ +class AudioPolicySetMicrophoneMuteTest : public AudioPolicyTest {}; + +HWTEST_P(AudioPolicySetMicrophoneMuteTest, SetMicrophoneMute, TestSize.Level1) +{ + PolicyParam params = GetParam(); + bool mute = params.mute; + + EXPECT_EQ(MEDIA_OK, AudioSystemManager::GetInstance()->SetMicrophoneMute(mute)); +} + +INSTANTIATE_TEST_CASE_P( + SetMicrophoneMute, + AudioPolicySetMicrophoneMuteTest, + ValuesIn(MIC_MUTE_PARAMS)); + +/* + * Is Microphone Mute + * + */ +class AudioPolicyGetMicrophoneMuteTest : public AudioPolicyTest {}; + +HWTEST_P(AudioPolicyGetMicrophoneMuteTest, IsMicrophoneMute, TestSize.Level1) +{ + PolicyParam params = GetParam(); + bool mute = params.mute; + + EXPECT_EQ(MEDIA_OK, AudioSystemManager::GetInstance()->SetMicrophoneMute(mute)); + EXPECT_EQ(mute, AudioSystemManager::GetInstance()->IsMicrophoneMute()); +} + +INSTANTIATE_TEST_CASE_P( + IsMicrophoneMute, + AudioPolicyGetMicrophoneMuteTest, + ValuesIn(MIC_MUTE_PARAMS)); + +/* + * Check volume range + * + */ +class AudioPolicyVolumeRangeTest : public AudioPolicyTest {}; + +HWTEST_P(AudioPolicyVolumeRangeTest, GetMaxVolume, TestSize.Level1) +{ + PolicyParam params = GetParam(); + AudioSystemManager::AudioVolumeType volumeType + = static_cast(params.streamType); + EXPECT_EQ(1.0, AudioSystemManager::GetInstance()->GetMaxVolume(volumeType)); +} + +HWTEST_P(AudioPolicyVolumeRangeTest, GetMinVolume, TestSize.Level1) +{ + PolicyParam params = GetParam(); + AudioSystemManager::AudioVolumeType volumeType + = static_cast(params.streamType); + EXPECT_EQ(0, AudioSystemManager::GetInstance()->GetMinVolume(volumeType)); +} + +INSTANTIATE_TEST_CASE_P( + GetMaxVolume, + AudioPolicyVolumeRangeTest, + ValuesIn(VOLUME_RANGE_PARAMS)); + +INSTANTIATE_TEST_CASE_P( + GetMinVolume, + AudioPolicyVolumeRangeTest, + ValuesIn(VOLUME_RANGE_PARAMS)); + +/* + * Check volume range + * + */ +class AudioPolicyAudioParameterTest : public AudioPolicyTest {}; + +HWTEST_P(AudioPolicyAudioParameterTest, SetAudioParameter, TestSize.Level1) +{ + PolicyParam params = GetParam(); + AudioSystemManager::GetInstance()->SetAudioParameter(params.key, params.value); + EXPECT_EQ(params.value, AudioSystemManager::GetInstance()->GetAudioParameter(params.key)); +} + +INSTANTIATE_TEST_CASE_P( + SetAudioParameter, + AudioPolicyAudioParameterTest, + ValuesIn(AUDIO_PARAMS)); + +HWTEST_P(AudioPolicyAudioParameterTest, GetAudioParameter, TestSize.Level1) +{ + PolicyParam params = GetParam(); + AudioSystemManager::GetInstance()->SetAudioParameter(params.key, params.value); + EXPECT_EQ(params.value, AudioSystemManager::GetInstance()->GetAudioParameter(params.key)); +} + +INSTANTIATE_TEST_CASE_P( + GetAudioParameter, + AudioPolicyAudioParameterTest, + ValuesIn(AUDIO_PARAMS)); + +/* + * Check volume range + * + */ +class AudioPolicyGetDevicesTest : public AudioPolicyTest {}; + +HWTEST_P(AudioPolicyGetDevicesTest, GetDevices, TestSize.Level1) +{ + PolicyParam params = GetParam(); + AudioDeviceDescriptor::DeviceFlag deviceFlag = static_cast(params.deviceFlag); + AudioDeviceDescriptor::DeviceType deviceType = static_cast(params.deviceType); + AudioDeviceDescriptor::DeviceRole deviceRole = static_cast(params.deviceRole); + vector> audioDeviceDescriptors + = AudioSystemManager::GetInstance()->GetDevices(deviceFlag); + sptr audioDeviceDescriptor = audioDeviceDescriptors[0]; + EXPECT_EQ(deviceType, audioDeviceDescriptor->deviceType_); + EXPECT_EQ(deviceRole, audioDeviceDescriptor->deviceRole_); +} + +INSTANTIATE_TEST_CASE_P( + GetDevices, + AudioPolicyGetDevicesTest, + ValuesIn(DEVICES_PARAMS)); +} +} +} diff --git a/frameworks/innerkitsimpl/test/moduletest/audiopolicy/BUILD.gn b/frameworks/innerkitsimpl/test/moduletest/audiopolicy/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..f1f218de0fb36b5e7bcde8754bec6cfd85c22577 --- /dev/null +++ b/frameworks/innerkitsimpl/test/moduletest/audiopolicy/BUILD.gn @@ -0,0 +1,42 @@ +# Copyright (C) 2021 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/test.gni") + +module_output_path = "audio_standard/audio_policy" + +group("moduletest") { + testonly = true + + deps = [ + ":audio_policy_module_test", + ] +} + +ohos_moduletest("audio_policy_module_test") { + module_out_path = module_output_path + include_dirs = [ + "./1.0/include", + "//foundation/multimedia/audio_standard/interfaces/innerkits/native/audiomanager/include", + "//base/hiviewdfx/hilog/interfaces/native/innerkits/include", + ] + + sources = [ + "1.0/src/audio_policy_test.cpp", + ] + + deps = [ + "//foundation/multimedia/audio_standard/interfaces/innerkits/native/audiomanager:audio_client", + "//utils/native/base:utils", + ] +} diff --git a/frameworks/innerkitsimpl/test/moduletest/recorder_test/BUILD.gn b/frameworks/innerkitsimpl/test/moduletest/recorder_test/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..2eb2888b5a746b2ac818901a84bc1c394deebcc1 --- /dev/null +++ b/frameworks/innerkitsimpl/test/moduletest/recorder_test/BUILD.gn @@ -0,0 +1,32 @@ +# Copyright (C) 2021 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/test.gni") + +module_output_path = "audio_standard/audio_recorder" + +ohos_moduletest("audio_recorder_module_test") { + module_out_path = module_output_path + include_dirs = [ + "./include", + "//foundation/multimedia/audio_standard/interfaces/innerkits/native/audiocommon/include", + ] + + sources = [ + "src/audio_recorder_module_test.cpp", + ] + + deps = [ + "//foundation/multimedia/audio_standard/interfaces/innerkits/native/audiorecorder:audio_recorder", + ] +} diff --git a/frameworks/innerkitsimpl/test/moduletest/recorder_test/include/audio_recorder_module_test.h b/frameworks/innerkitsimpl/test/moduletest/recorder_test/include/audio_recorder_module_test.h new file mode 100644 index 0000000000000000000000000000000000000000..a5a11c8d90bcf5d073d41271c8a27f8597d7d958 --- /dev/null +++ b/frameworks/innerkitsimpl/test/moduletest/recorder_test/include/audio_recorder_module_test.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2021 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_recorder.h" +#include "gtest/gtest.h" + +namespace OHOS { +namespace AudioStandard { +class AudioRecorderModuleTest : public testing::Test { +public: + // SetUpTestCase: Called before all test cases + static void SetUpTestCase(void); + // TearDownTestCase: Called after all test case + static void TearDownTestCase(void); + // SetUp: Called before each test cases + void SetUp(void); + // TearDown: Called after each test cases + void TearDown(void); + // Init Recorder + static int32_t InitializeRecorder(std::unique_ptr &audioRecorder); +}; +} // namespace AudioStandard +} // namespace OHOS diff --git a/frameworks/innerkitsimpl/test/moduletest/recorder_test/src/audio_recorder_module_test.cpp b/frameworks/innerkitsimpl/test/moduletest/recorder_test/src/audio_recorder_module_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..bc6446867db33902830ca1178f45d17265483e8f --- /dev/null +++ b/frameworks/innerkitsimpl/test/moduletest/recorder_test/src/audio_recorder_module_test.cpp @@ -0,0 +1,568 @@ +/* + * Copyright (C) 2021 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_recorder_module_test.h" + + +#include "audio_info.h" +#include "audio_recorder.h" + +using namespace std; +using namespace testing::ext; + +namespace OHOS { +namespace AudioStandard { +namespace { + const string AUDIO_RECORD_FILE1 = "/data/audioreordtest_blocking.pcm"; + const string AUDIO_RECORD_FILE2 = "/data/audioreordtest_nonblocking.pcm"; + const string AUDIO_RECORD_FILE3 = "/data/audioreordtest_out.pcm"; + const int32_t READ_BUFFERS_COUNT = 128; + const int32_t VALUE_ZERO = 0; + const int32_t SUCCESS = 0; +} // namespace + +void AudioRecorderModuleTest::SetUpTestCase(void) {} +void AudioRecorderModuleTest::TearDownTestCase(void) {} +void AudioRecorderModuleTest::SetUp(void) {} +void AudioRecorderModuleTest::TearDown(void) {} + +int32_t AudioRecorderModuleTest::InitializeRecorder(unique_ptr &audioRecorder) +{ + AudioRecorderParams recorderParams; + recorderParams.audioSampleFormat = SAMPLE_S16LE; + recorderParams.samplingRate = SAMPLE_RATE_44100; + recorderParams.audioChannel = STEREO; + recorderParams.audioEncoding = ENCODING_PCM; + + return audioRecorder->SetParams(recorderParams); +} + +/** +* @tc.name : Test GetSupportedFormats API +* @tc.number: Auido_Recorder_GetSupportedFormats_001 +* @tc.desc : test GetSupportedFormats interface, Returns supported Formats on success. +*/ +HWTEST(AudioRecorderModuleTest, Auido_Recorder_GetSupportedFormats_001, TestSize.Level1) +{ + vector supportedFormatList = AudioRecorder::GetSupportedFormats(); + EXPECT_EQ(AUDIO_SUPPORTED_FORMATS.size(), supportedFormatList.size()); +} + +/** +* @tc.name : Test GetSupportedChannels API +* @tc.number: Auido_Recorder_GetSupportedChannels_001 +* @tc.desc : test GetSupportedChannels interface, Returns supported Channels on success. +*/ +HWTEST(AudioRecorderModuleTest, Auido_Recorder_GetSupportedChannels_001, TestSize.Level1) +{ + vector supportedChannelList = AudioRecorder::GetSupportedChannels(); + EXPECT_EQ(AUDIO_SUPPORTED_CHANNELS.size(), supportedChannelList.size()); +} + +/** +* @tc.name : Test GetSupportedEncodingTypes API +* @tc.number: Auido_Recorder_GetSupportedEncodingTypes_001 +* @tc.desc : test GetSupportedEncodingTypes interface, Returns supported Encoding types on success. +*/ +HWTEST(AudioRecorderModuleTest, Auido_Recorder_GetSupportedEncodingTypes_001, TestSize.Level1) +{ + vector supportedEncodingTypes + = AudioRecorder::GetSupportedEncodingTypes(); + EXPECT_EQ(AUDIO_SUPPORTED_ENCODING_TYPES.size(), supportedEncodingTypes.size()); +} + +/** +* @tc.name : Test GetSupportedSamplingRates API +* @tc.number: Auido_Recorder_GetSupportedSamplingRates_001 +* @tc.desc : test GetSupportedSamplingRates interface, Returns supported Sampling rates on success. +*/ +HWTEST(AudioRecoderModuleTest, Auido_Recorder_GetSupportedSamplingRates_001, TestSize.Level1) +{ + vector supportedSamplingRates = AudioRecorder::GetSupportedSamplingRates(); + EXPECT_EQ(AUDIO_SUPPORTED_SAMPLING_RATES.size(), supportedSamplingRates.size()); +} + +/** +* @tc.name : Test Create API via legal input. +* @tc.number: Auido_Recorder_Create_001 +* @tc.desc : test Create interface, Returns audioRecorder instance if create is Successful. +*/ +HWTEST(AudioRecorderModuleTest, Auido_Recorder_Create_001, TestSize.Level1) +{ + unique_ptr audioRecorder = AudioRecorder::Create(STREAM_MUSIC); + EXPECT_NE(nullptr, audioRecorder); +} + +/** +* @tc.name : Test SetParams API via legal input. +* @tc.number: Auido_Recorder_SetParams_001 +* @tc.desc : test SetParams interface, Returns 0 {SUCCESS} if the setting is successful. +*/ +HWTEST(AudioRecorderModuleTest, Auido_Recorder_SetParams_001, TestSize.Level1) +{ + unique_ptr audioRecorder = AudioRecorder::Create(STREAM_MUSIC); + ASSERT_NE(nullptr, audioRecorder); + + AudioRecorderParams recorderParams; + recorderParams.audioSampleFormat = SAMPLE_S16LE; + recorderParams.samplingRate = SAMPLE_RATE_44100; + recorderParams.audioChannel = STEREO; + recorderParams.audioEncoding = ENCODING_PCM; + + int32_t ret = audioRecorder->SetParams(recorderParams); + EXPECT_EQ(SUCCESS, ret); + audioRecorder->Release(); +} + +/** +* @tc.name : Test GetParams API via legal input. +* @tc.number: Auido_Recorder_GetParams_001 +* @tc.desc : test GetParams interface, Returns 0 {SUCCESS} if the getting is successful. +*/ +HWTEST(AudioRecorderModuleTest, Auido_Recorder_GetParams_001, TestSize.Level1) +{ + int32_t ret = -1; + unique_ptr audioRecorder = AudioRecorder::Create(STREAM_MUSIC); + ASSERT_NE(nullptr, audioRecorder); + + AudioRecorderParams recorderParams; + recorderParams.audioSampleFormat = SAMPLE_S16LE; + recorderParams.samplingRate = SAMPLE_RATE_44100; + recorderParams.audioChannel = STEREO; + recorderParams.audioEncoding = ENCODING_PCM; + ret = audioRecorder->SetParams(recorderParams); + EXPECT_EQ(SUCCESS, ret); + + AudioRecorderParams getRecorderParams; + ret = audioRecorder->GetParams(getRecorderParams); + EXPECT_EQ(SUCCESS, ret); + EXPECT_EQ(recorderParams.audioSampleFormat, getRecorderParams.audioSampleFormat); + EXPECT_EQ(recorderParams.samplingRate, getRecorderParams.samplingRate); + EXPECT_EQ(recorderParams.audioChannel, getRecorderParams.audioChannel); + EXPECT_EQ(recorderParams.audioEncoding, getRecorderParams.audioEncoding); + + audioRecorder->Release(); +} + +/** +* @tc.name : Test GetBufferSize API via legal input. +* @tc.number: Auido_Recorder_GetBufferSize_001 +* @tc.desc : test GetBufferSize interface, Returns 0 {SUCCESS} if the getting is successful. +*/ +HWTEST(AudioRecorderModuleTest, Auido_Recorder_GetBufferSize_001, TestSize.Level1) +{ + int32_t ret = -1; + unique_ptr audioRecorder = AudioRecorder::Create(STREAM_MUSIC); + EXPECT_NE(nullptr, audioRecorder); + + ret = AudioRecorderModuleTest::InitializeRecorder(audioRecorder); + EXPECT_EQ(SUCCESS, ret); + size_t bufferLen; + ret = audioRecorder->GetBufferSize(bufferLen); + EXPECT_EQ(SUCCESS, ret); + + audioRecorder->Release(); +} + +/** +* @tc.name : Test GetFrameCount API via legal input. +* @tc.number: Auido_Recorder_GetFrameCount_001 +* @tc.desc : test GetFrameCount interface, Returns 0 {SUCCESS} if the getting is successful. +*/ +HWTEST(AudioRecorderModuleTest, Auido_Recorder_GetFrameCount_001, TestSize.Level1) +{ + int32_t ret = -1; + unique_ptr audioRecorder = AudioRecorder::Create(STREAM_MUSIC); + EXPECT_NE(nullptr, audioRecorder); + + ret = AudioRecorderModuleTest::InitializeRecorder(audioRecorder); + EXPECT_EQ(SUCCESS, ret); + + uint32_t frameCount; + ret = audioRecorder->GetFrameCount(frameCount); + EXPECT_EQ(SUCCESS, ret); + + audioRecorder->Release(); +} + +/** +* @tc.name : Test Start API. +* @tc.number: Audio_Recorder_Start_001 +* @tc.desc : test Start interface, Returns true if start is successful. +*/ +HWTEST(AudioRecorderModuleTest, Audio_Recorder_Start_001, TestSize.Level1) +{ + int32_t ret = -1; + unique_ptr audioRecorder = AudioRecorder::Create(STREAM_MUSIC); + EXPECT_NE(nullptr, audioRecorder); + + ret = AudioRecorderModuleTest::InitializeRecorder(audioRecorder); + EXPECT_EQ(SUCCESS, ret); + + bool isStarted = audioRecorder->Start(); + EXPECT_EQ(true, isStarted); + + audioRecorder->Release(); +} + +/** +* @tc.name : Test Read API via isBlockingRead = true. +* @tc.number: Audio_Recorder_Read_001 +* @tc.desc : test GetFrameCount interface, Returns number of bytes read if the read is successful. +*/ +HWTEST(AudioRecorderModuleTest, Audio_Recorder_Read_001, TestSize.Level1) +{ + int32_t ret = -1; + bool isBlockingRead = true; + unique_ptr audioRecorder = AudioRecorder::Create(STREAM_MUSIC); + EXPECT_NE(nullptr, audioRecorder); + + ret = AudioRecorderModuleTest::InitializeRecorder(audioRecorder); + EXPECT_EQ(SUCCESS, ret); + + bool isStarted = audioRecorder->Start(); + EXPECT_EQ(true, isStarted); + + size_t bufferLen; + ret = audioRecorder->GetBufferSize(bufferLen); + EXPECT_EQ(SUCCESS, ret); + + uint8_t* buffer = (uint8_t *) malloc(bufferLen); + ASSERT_NE(nullptr, buffer); + FILE *recFile = fopen(AUDIO_RECORD_FILE1.c_str(), "wb"); + ASSERT_NE(nullptr, recFile); + + size_t size = 1; + int32_t bytesRead = 0; + int32_t numBuffersToRecord = READ_BUFFERS_COUNT; + + while (numBuffersToRecord) { + bytesRead = audioRecorder->Read(*buffer, bufferLen, isBlockingRead); + if (bytesRead < 0) { + break; + } else if (bytesRead > 0) { + fwrite(buffer, size, bytesRead, recFile); + numBuffersToRecord--; + } + } + + audioRecorder->Flush(); + audioRecorder->Stop(); + audioRecorder->Release(); + + free(buffer); + fclose(recFile); +} + +/** +* @tc.name : Test Read API via isBlockingRead = false. +* @tc.number: Audio_Recorder_Read_002 +* @tc.desc : test GetFrameCount interface, Returns number of bytes read if the read is successful. +*/ +HWTEST(AudioRecorderModuleTest, Audio_Recorder_Read_002, TestSize.Level1) +{ + int32_t ret = -1; + bool isBlockingRead = false; + unique_ptr audioRecorder = AudioRecorder::Create(STREAM_MUSIC); + EXPECT_NE(nullptr, audioRecorder); + + ret = AudioRecorderModuleTest::InitializeRecorder(audioRecorder); + EXPECT_EQ(SUCCESS, ret); + + bool isStarted = audioRecorder->Start(); + EXPECT_EQ(true, isStarted); + + size_t bufferLen; + ret = audioRecorder->GetBufferSize(bufferLen); + EXPECT_EQ(SUCCESS, ret); + + uint8_t* buffer = (uint8_t *) malloc(bufferLen); + ASSERT_NE(nullptr, buffer); + FILE *recFile = fopen(AUDIO_RECORD_FILE2.c_str(), "wb"); + ASSERT_NE(nullptr, recFile); + + size_t size = 1; + int32_t bytesRead = 0; + int32_t numBuffersToRecord = READ_BUFFERS_COUNT; + + while (numBuffersToRecord) { + bytesRead = audioRecorder->Read(*buffer, bufferLen, isBlockingRead); + if (bytesRead < 0) { + break; + } else if (bytesRead > 0) { + fwrite(buffer, size, bytesRead, recFile); + numBuffersToRecord--; + } + } + + audioRecorder->Flush(); + audioRecorder->Stop(); + audioRecorder->Release(); + + free(buffer); + fclose(recFile); +} + +/** +* @tc.name : Test GetAudioTime API via legal input. +* @tc.number: Auido_Recorder_GetAudioTime_001 +* @tc.desc : test GetAudioTime interface, Returns true if the getting is successful. +*/ +HWTEST(AudioRecorderModuleTest, Auido_Recorder_GetAudioTime_001, TestSize.Level1) +{ + int32_t ret = -1; + bool isBlockingRead = true; + unique_ptr audioRecorder = AudioRecorder::Create(STREAM_MUSIC); + EXPECT_NE(nullptr, audioRecorder); + + ret = AudioRecorderModuleTest::InitializeRecorder(audioRecorder); + EXPECT_EQ(SUCCESS, ret); + + bool isStarted = audioRecorder->Start(); + EXPECT_EQ(true, isStarted); + + size_t bufferLen; + ret = audioRecorder->GetBufferSize(bufferLen); + EXPECT_EQ(SUCCESS, ret); + + uint8_t* buffer = (uint8_t *) malloc(bufferLen); + ASSERT_NE(nullptr, buffer); + FILE *recFile = fopen(AUDIO_RECORD_FILE3.c_str(), "wb"); + ASSERT_NE(nullptr, recFile); + + size_t size = 1; + int32_t bytesRead = 0; + int32_t numBuffersToRecord = 5; + + while (numBuffersToRecord) { + bytesRead = audioRecorder->Read(*buffer, bufferLen, isBlockingRead); + if (bytesRead < 0) { + break; + } else if (bytesRead > 0) { + fwrite(buffer, size, bytesRead, recFile); + numBuffersToRecord--; + } + } + + Timestamp timeStamp; + bool getAudioTime = audioRecorder->GetAudioTime(timeStamp, Timestamp::Timestampbase::MONOTONIC); + EXPECT_EQ(true, getAudioTime); + EXPECT_GE(timeStamp.time.tv_sec, (const long)VALUE_ZERO); + EXPECT_GE(timeStamp.time.tv_nsec, (const long)VALUE_ZERO); + + audioRecorder->Flush(); + audioRecorder->Stop(); + audioRecorder->Release(); + + free(buffer); + fclose(recFile); +} + +/** +* @tc.name : Test Flush API. +* @tc.number: Auido_Recorder_Flush_001 +* @tc.desc : test Flush interface, Returns true if the flush is successful. +*/ +HWTEST(AudioRecorderModuleTest, Auido_Recorder_Flush_001, TestSize.Level1) +{ + int32_t ret = -1; + bool isBlockingRead = true; + unique_ptr audioRecorder = AudioRecorder::Create(STREAM_MUSIC); + EXPECT_NE(nullptr, audioRecorder); + + ret = AudioRecorderModuleTest::InitializeRecorder(audioRecorder); + EXPECT_EQ(SUCCESS, ret); + + bool isStarted = audioRecorder->Start(); + EXPECT_EQ(true, isStarted); + + size_t bufferLen; + ret = audioRecorder->GetBufferSize(bufferLen); + EXPECT_EQ(SUCCESS, ret); + + uint8_t* buffer = (uint8_t *) malloc(bufferLen); + ASSERT_NE(nullptr, buffer); + FILE *recFile = fopen(AUDIO_RECORD_FILE3.c_str(), "wb"); + ASSERT_NE(nullptr, recFile); + + size_t size = 1; + int32_t bytesRead = 0; + int32_t numBuffersToRecord = 5; + + while (numBuffersToRecord) { + bytesRead = audioRecorder->Read(*buffer, bufferLen, isBlockingRead); + if (bytesRead < 0) { + break; + } else if (bytesRead > 0) { + fwrite(buffer, size, bytesRead, recFile); + numBuffersToRecord--; + } + } + + bool isFlushed = audioRecorder->Flush(); + EXPECT_EQ(true, isFlushed); + + audioRecorder->Stop(); + audioRecorder->Release(); + + free(buffer); + fclose(recFile); +} + +/** +* @tc.name : Test Stop API. +* @tc.number: Auido_Recorder_Stop_001 +* @tc.desc : test Stop interface, Returns true if the stop is successful. +*/ +HWTEST(AudioRecorderModuleTest, Auido_Recorder_Stop_001, TestSize.Level1) +{ + int32_t ret = -1; + bool isBlockingRead = true; + unique_ptr audioRecorder = AudioRecorder::Create(STREAM_MUSIC); + EXPECT_NE(nullptr, audioRecorder); + + ret = AudioRecorderModuleTest::InitializeRecorder(audioRecorder); + EXPECT_EQ(SUCCESS, ret); + + bool isStarted = audioRecorder->Start(); + EXPECT_EQ(true, isStarted); + + size_t bufferLen; + ret = audioRecorder->GetBufferSize(bufferLen); + EXPECT_EQ(SUCCESS, ret); + + uint8_t* buffer = (uint8_t *) malloc(bufferLen); + ASSERT_NE(nullptr, buffer); + FILE *recFile = fopen(AUDIO_RECORD_FILE3.c_str(), "wb"); + ASSERT_NE(nullptr, recFile); + + size_t size = 1; + int32_t bytesRead = 0; + int32_t numBuffersToRecord = 5; + + while (numBuffersToRecord) { + bytesRead = audioRecorder->Read(*buffer, bufferLen, isBlockingRead); + if (bytesRead < 0) { + break; + } else if (bytesRead > 0) { + fwrite(buffer, size, bytesRead, recFile); + numBuffersToRecord--; + } + } + + audioRecorder->Flush(); + + bool isStopped = audioRecorder->Stop(); + EXPECT_EQ(true, isStopped); + + audioRecorder->Release(); + + free(buffer); + fclose(recFile); +} + +/** +* @tc.name : Test Release API. +* @tc.number: Auido_Recorder_Release_001 +* @tc.desc : test Release interface, Returns true if the release is successful. +*/ +HWTEST(AudioRecorderModuleTest, Auido_Recorder_Release_001, TestSize.Level1) +{ + int32_t ret = -1; + bool isBlockingRead = true; + unique_ptr audioRecorder = AudioRecorder::Create(STREAM_MUSIC); + EXPECT_NE(nullptr, audioRecorder); + + ret = AudioRecorderModuleTest::InitializeRecorder(audioRecorder); + EXPECT_EQ(SUCCESS, ret); + + bool isStarted = audioRecorder->Start(); + EXPECT_EQ(true, isStarted); + + size_t bufferLen; + ret = audioRecorder->GetBufferSize(bufferLen); + EXPECT_EQ(SUCCESS, ret); + + uint8_t* buffer = (uint8_t *) malloc(bufferLen); + ASSERT_NE(nullptr, buffer); + FILE *recFile = fopen(AUDIO_RECORD_FILE3.c_str(), "wb"); + ASSERT_NE(nullptr, recFile); + + size_t size = 1; + int32_t bytesRead = 0; + int32_t numBuffersToRecord = 5; + + while (numBuffersToRecord) { + bytesRead = audioRecorder->Read(*buffer, bufferLen, isBlockingRead); + if (bytesRead < 0) { + break; + } else if (bytesRead > 0) { + fwrite(buffer, size, bytesRead, recFile); + numBuffersToRecord--; + } + } + + audioRecorder->Flush(); + audioRecorder->Stop(); + + bool isReleased = audioRecorder->Release(); + EXPECT_EQ(true, isReleased); + + free(buffer); + fclose(recFile); +} + +/** +* @tc.name : Test GetStatus API. +* @tc.number: Auido_Recorder_GetStatus_001 +* @tc.desc : test GetStatus interface, Returns correct state on success. +*/ +HWTEST(AudioRecorderModuleTest, Audio_Recorder_GetStatus_001, TestSize.Level1) +{ + int32_t ret = -1; + RecorderState state = RECORDER_INVALID; + + unique_ptr audioRecorder = AudioRecorder::Create(STREAM_MUSIC); + ASSERT_NE(nullptr, audioRecorder); + state = audioRecorder->GetStatus(); + EXPECT_EQ(RECORDER_NEW, state); + + AudioRecorderParams recorderParams; + recorderParams.audioSampleFormat = SAMPLE_S16LE; + recorderParams.samplingRate = SAMPLE_RATE_44100; + recorderParams.audioChannel = STEREO; + recorderParams.audioEncoding = ENCODING_PCM; + ret = audioRecorder->SetParams(recorderParams); + EXPECT_EQ(SUCCESS, ret); + state = audioRecorder->GetStatus(); + EXPECT_EQ(RECORDER_PREPARED, state); + + bool isStarted = audioRecorder->Start(); + EXPECT_EQ(true, isStarted); + state = audioRecorder->GetStatus(); + EXPECT_EQ(RECORDER_RUNNING, state); + + bool isStopped = audioRecorder->Stop(); + EXPECT_EQ(true, isStopped); + state = audioRecorder->GetStatus(); + EXPECT_EQ(RECORDER_STOPPED, state); + + bool isReleased = audioRecorder->Release(); + EXPECT_EQ(true, isReleased); + state = audioRecorder->GetStatus(); + EXPECT_EQ(RECORDER_RELEASED, state); +} +} // namespace AudioStandard +} // namespace OHOS diff --git a/frameworks/innerkitsimpl/test/moduletest/renderer_test/BUILD.gn b/frameworks/innerkitsimpl/test/moduletest/renderer_test/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..e4a05f69f52a1b26a166d5380af8b756fe8f4936 --- /dev/null +++ b/frameworks/innerkitsimpl/test/moduletest/renderer_test/BUILD.gn @@ -0,0 +1,40 @@ +# Copyright (C) 2021 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/test.gni") + +module_output_path = "audio_standard/audio_renderer" + +group("moduletest") { + testonly = true + + deps = [ + ":audio_renderer_module_test", + ] +} + +ohos_moduletest("audio_renderer_module_test") { + module_out_path = module_output_path + include_dirs = [ + "./include", + "//foundation/multimedia/audio_standard/interfaces/innerkits/native/audiocommon/include", + ] + + sources = [ + "src/audio_renderer_module_test.cpp", + ] + + deps = [ + "//foundation/multimedia/audio_standard/interfaces/innerkits/native/audiorenderer:audio_renderer", + ] +} \ No newline at end of file diff --git a/frameworks/innerkitsimpl/test/moduletest/renderer_test/include/audio_renderer_module_test.h b/frameworks/innerkitsimpl/test/moduletest/renderer_test/include/audio_renderer_module_test.h new file mode 100644 index 0000000000000000000000000000000000000000..d1f535a685dcb85e546cfd24817741479121b491 --- /dev/null +++ b/frameworks/innerkitsimpl/test/moduletest/renderer_test/include/audio_renderer_module_test.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2021 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 "gtest/gtest.h" + +class AudioRendererModuleTest : public testing::Test { +public: + // SetUpTestCase: Called before all test cases + static void SetUpTestCase(void); + // TearDownTestCase: Called after all test case + static void TearDownTestCase(void); + // SetUp: Called before each test cases + void SetUp(void); + // TearDown: Called after each test cases + void TearDown(void); +}; \ No newline at end of file diff --git a/frameworks/innerkitsimpl/test/moduletest/renderer_test/src/audio_renderer_module_test.cpp b/frameworks/innerkitsimpl/test/moduletest/renderer_test/src/audio_renderer_module_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1d817190dee91c61d8fdb002dd8b38531f95a719 --- /dev/null +++ b/frameworks/innerkitsimpl/test/moduletest/renderer_test/src/audio_renderer_module_test.cpp @@ -0,0 +1,540 @@ +/* + * Copyright (C) 2021 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_renderer_module_test.h" + +#include "audio_renderer.h" + +using namespace std; +using namespace OHOS::AudioStandard; +using namespace testing::ext; + +namespace RendererModuleTestConstants { + const char *AUDIO_TEST_FILE_PATH = "/data/test_44100_2.wav"; + const unsigned int ZERO = 0; + // Writing only 500 KB of data for test + const uint64_t WRITE_SIZE_LIMIT = 500 * 1024; +} + +void AudioRendererModuleTest::SetUpTestCase(void) {} +void AudioRendererModuleTest::TearDownTestCase(void) {} +void AudioRendererModuleTest::SetUp(void) {} +void AudioRendererModuleTest::TearDown(void) {} + +/* + * Feature: Audio render + * Function: Playback + * SubFunction: NA + * FunctionPoints: NA + * EnvConditions: NA + * CaseDescription: Get supported formats + */ +HWTEST(AudioRendererModuleTest, audio_renderer_get_supported_formats_001, TestSize.Level1) +{ + vector supportedFormatList = AudioRenderer::GetSupportedFormats(); + EXPECT_EQ(AUDIO_SUPPORTED_FORMATS.size(), supportedFormatList.size()); +} + +/* + * Feature: Audio render + * Function: Playback + * SubFunction: NA + * FunctionPoints: NA + * EnvConditions: NA + * CaseDescription: Get supported channels + */ +HWTEST(AudioRendererModuleTest, audio_renderer_get_supported_channels_001, TestSize.Level1) +{ + vector supportedChannelList = AudioRenderer::GetSupportedChannels(); + EXPECT_EQ(AUDIO_SUPPORTED_CHANNELS.size(), supportedChannelList.size()); +} + +/* + * Feature: Audio render + * Function: Playback + * SubFunction: NA + * FunctionPoints: NA + * EnvConditions: NA + * CaseDescription: Get supported channels + */ +HWTEST(AudioRendererModuleTest, audio_renderer_get_supported_encoding_types_001, TestSize.Level1) +{ + vector supportedEncodingTypes + = AudioRenderer::GetSupportedEncodingTypes(); + EXPECT_EQ(AUDIO_SUPPORTED_ENCODING_TYPES.size(), supportedEncodingTypes.size()); +} + +/* + * Feature: Audio render + * Function: Playback + * SubFunction: NA + * FunctionPoints: NA + * EnvConditions: NA + * CaseDescription: Get supported sampling rates + */ +HWTEST(AudioRendererModuleTest, audio_renderer_get_supported_sampling_rates_001, TestSize.Level1) +{ + vector supportedSamplingRates = AudioRenderer::GetSupportedSamplingRates(); + EXPECT_EQ(AUDIO_SUPPORTED_SAMPLING_RATES.size(), supportedSamplingRates.size()); +} + +/* + * Feature: Audio render + * Function: Playback + * SubFunction: NA + * FunctionPoints: NA + * EnvConditions: NA + * CaseDescription: Create renderer instance + */ +HWTEST(AudioRendererModuleTest, audio_renderer_create_001, TestSize.Level1) +{ + unique_ptr audioRenderer = AudioRenderer::Create(AudioStreamType::STREAM_MUSIC); + EXPECT_NE(nullptr, audioRenderer); +} + +/* + * Feature: Audio render + * Function: Playback + * SubFunction: NA + * FunctionPoints: NA + * EnvConditions: NA + * CaseDescription: Set renderer parameters + */ +HWTEST(AudioRendererModuleTest, audio_renderer_set_params_001, TestSize.Level1) +{ + int32_t ret = 0; + unique_ptr audioRenderer = AudioRenderer::Create(AudioStreamType::STREAM_MUSIC); + AudioRendererParams rendererParams; + rendererParams.sampleFormat = AudioSampleFormat::SAMPLE_S16LE; + rendererParams.sampleRate = AudioSamplingRate::SAMPLE_RATE_44100; + rendererParams.channelCount = AudioChannel::STEREO; + rendererParams.encodingType = AudioEncodingType::ENCODING_PCM; + + ret = audioRenderer->SetParams(rendererParams); + EXPECT_EQ(0, ret); + audioRenderer->Release(); +} + +/* + * Feature: Audio render + * Function: Playback + * SubFunction: NA + * FunctionPoints: NA + * EnvConditions: NA + * CaseDescription: Get renderer parameters + */ +HWTEST(AudioRendererModuleTest, audio_renderer_get_params_001, TestSize.Level1) +{ + int32_t ret = 0; + unique_ptr audioRenderer = AudioRenderer::Create(AudioStreamType::STREAM_MUSIC); + AudioRendererParams rendererParams; + rendererParams.sampleFormat = AudioSampleFormat::SAMPLE_S16LE; + rendererParams.sampleRate = AudioSamplingRate::SAMPLE_RATE_44100; + rendererParams.channelCount = AudioChannel::STEREO; + rendererParams.encodingType = AudioEncodingType::ENCODING_PCM; + + ret = audioRenderer->SetParams(rendererParams); + EXPECT_EQ(0, ret); + + AudioRendererParams paRendererParams; + ret = audioRenderer->GetParams(paRendererParams); + EXPECT_EQ(0, ret); + EXPECT_EQ(paRendererParams.sampleFormat, rendererParams.sampleFormat); + EXPECT_EQ(paRendererParams.sampleRate, rendererParams.sampleRate); + EXPECT_EQ(paRendererParams.channelCount, rendererParams.channelCount); + EXPECT_EQ(paRendererParams.encodingType, rendererParams.encodingType); + + audioRenderer->Release(); +} + +/* + * Feature: Audio render + * Function: Playback + * SubFunction: NA + * FunctionPoints: NA + * EnvConditions: NA + * CaseDescription: Get buffer size + */ +HWTEST(AudioRendererModuleTest, audio_renderer_get_buffer_size_001, TestSize.Level1) +{ + int32_t ret = 0; + unique_ptr audioRenderer = AudioRenderer::Create(AudioStreamType::STREAM_MUSIC); + AudioRendererParams rendererParams; + rendererParams.sampleFormat = AudioSampleFormat::SAMPLE_S16LE; + rendererParams.sampleRate = AudioSamplingRate::SAMPLE_RATE_44100; + rendererParams.channelCount = AudioChannel::STEREO; + rendererParams.encodingType = AudioEncodingType::ENCODING_PCM; + + ret = audioRenderer->SetParams(rendererParams); + EXPECT_EQ(0, ret); + size_t bufferLen; + ret = audioRenderer->GetBufferSize(bufferLen); + EXPECT_EQ(0, ret); + + audioRenderer->Release(); +} + +/* + * Feature: Audio render + * Function: Playback + * SubFunction: NA + * FunctionPoints: NA + * EnvConditions: NA + * CaseDescription: Get frame count + */ +HWTEST(AudioRendererModuleTest, audio_renderer_get_frame_count_001, TestSize.Level1) +{ + int32_t ret = 0; + unique_ptr audioRenderer = AudioRenderer::Create(AudioStreamType::STREAM_MUSIC); + AudioRendererParams rendererParams; + rendererParams.sampleFormat = AudioSampleFormat::SAMPLE_S16LE; + rendererParams.sampleRate = AudioSamplingRate::SAMPLE_RATE_44100; + rendererParams.channelCount = AudioChannel::STEREO; + rendererParams.encodingType = AudioEncodingType::ENCODING_PCM; + + ret = audioRenderer->SetParams(rendererParams); + EXPECT_EQ(0, ret); + uint32_t frameCount; + ret = audioRenderer->GetFrameCount(frameCount); + EXPECT_EQ(0, ret); + + audioRenderer->Release(); +} + +/* + * Feature: Audio render + * Function: Playback + * SubFunction: NA + * FunctionPoints: NA + * EnvConditions: NA + * CaseDescription: Start audio renderer + */ +HWTEST(AudioRendererModuleTest, audio_renderer_start_001, TestSize.Level1) +{ + int32_t ret = 0; + unique_ptr audioRenderer = AudioRenderer::Create(AudioStreamType::STREAM_MUSIC); + AudioRendererParams rendererParams; + rendererParams.sampleFormat = AudioSampleFormat::SAMPLE_S16LE; + rendererParams.sampleRate = AudioSamplingRate::SAMPLE_RATE_44100; + rendererParams.channelCount = AudioChannel::STEREO; + rendererParams.encodingType = AudioEncodingType::ENCODING_PCM; + + ret = audioRenderer->SetParams(rendererParams); + EXPECT_EQ(0, ret); + bool isStarted = audioRenderer->Start(); + EXPECT_EQ(true, isStarted); + + audioRenderer->Release(); +} + +/* + * Feature: Audio render + * Function: Playback + * SubFunction: NA + * FunctionPoints: NA + * EnvConditions: NA + * CaseDescription: write audio renderer + */ +HWTEST(AudioRendererModuleTest, audio_renderer_write_001, TestSize.Level1) +{ + int32_t ret = 0; + + FILE *wavFile = fopen(RendererModuleTestConstants::AUDIO_TEST_FILE_PATH, "rb"); + EXPECT_NE(nullptr, wavFile); + + unique_ptr audioRenderer = AudioRenderer::Create(AudioStreamType::STREAM_MUSIC); + AudioRendererParams rendererParams; + rendererParams.sampleFormat = AudioSampleFormat::SAMPLE_S16LE; + rendererParams.sampleRate = AudioSamplingRate::SAMPLE_RATE_44100; + rendererParams.channelCount = AudioChannel::STEREO; + rendererParams.encodingType = AudioEncodingType::ENCODING_PCM; + + ret = audioRenderer->SetParams(rendererParams); + EXPECT_EQ(0, ret); + bool isStarted = audioRenderer->Start(); + EXPECT_EQ(true, isStarted); + size_t bufferLen; + ret = audioRenderer->GetBufferSize(bufferLen); + EXPECT_EQ(0, ret); + + uint8_t *buffer = nullptr; + int32_t n = 2; + buffer = (uint8_t *) malloc(n * bufferLen); + size_t bytesToWrite = 0; + size_t bytesWritten = 0; + size_t minBytes = 4; + size_t tempBytesWritten = 0; + size_t totalBytesWritten = 0; + while (!feof(wavFile)) { + bytesToWrite = fread(buffer, 1, bufferLen, wavFile); + bytesWritten = 0; + while ((bytesWritten < bytesToWrite) && ((bytesToWrite - bytesWritten) > minBytes)) { + tempBytesWritten = audioRenderer->Write(buffer + bytesWritten, + bytesToWrite - bytesWritten); + EXPECT_GE(tempBytesWritten, RendererModuleTestConstants::ZERO); + bytesWritten += tempBytesWritten; + } + totalBytesWritten += bytesWritten; + if (totalBytesWritten >= RendererModuleTestConstants::WRITE_SIZE_LIMIT) { + break; + } + } + + audioRenderer->Drain(); + audioRenderer->Release(); + + free(buffer); + fclose(wavFile); +} + +/* + * Feature: Audio render + * Function: Playback + * SubFunction: NA + * FunctionPoints: NA + * EnvConditions: NA + * CaseDescription: Get audio time + */ +HWTEST(AudioRendererModuleTest, audio_renderer_get_audio_time_001, TestSize.Level1) +{ + int32_t ret = 0; + + FILE *wavFile = fopen(RendererModuleTestConstants::AUDIO_TEST_FILE_PATH, "rb"); + EXPECT_NE(nullptr, wavFile); + + unique_ptr audioRenderer = AudioRenderer::Create(AudioStreamType::STREAM_MUSIC); + AudioRendererParams rendererParams; + rendererParams.sampleFormat = AudioSampleFormat::SAMPLE_S16LE; + rendererParams.sampleRate = AudioSamplingRate::SAMPLE_RATE_44100; + rendererParams.channelCount = AudioChannel::STEREO; + rendererParams.encodingType = AudioEncodingType::ENCODING_PCM; + + ret = audioRenderer->SetParams(rendererParams); + EXPECT_EQ(0, ret); + bool isStarted = audioRenderer->Start(); + EXPECT_EQ(true, isStarted); + size_t bufferLen; + ret = audioRenderer->GetBufferSize(bufferLen); + EXPECT_EQ(0, ret); + + uint8_t *buffer = nullptr; + int32_t n = 2; + buffer = (uint8_t *) malloc(n * bufferLen); + size_t bytesToWrite = 0; + size_t bytesWritten = 0; + size_t minBytes = 4; + size_t tempBytesWritten = 0; + size_t totalBytesWritten = 0; + while (!feof(wavFile)) { + bytesToWrite = fread(buffer, 1, bufferLen, wavFile); + bytesWritten = 0; + while ((bytesWritten < bytesToWrite) && ((bytesToWrite - bytesWritten) > minBytes)) { + tempBytesWritten = audioRenderer->Write(buffer + bytesWritten, + bytesToWrite - bytesWritten); + EXPECT_GE(tempBytesWritten, RendererModuleTestConstants::ZERO); + bytesWritten += tempBytesWritten; + } + totalBytesWritten += bytesWritten; + if (totalBytesWritten >= RendererModuleTestConstants::WRITE_SIZE_LIMIT) { + break; + } + } + + audioRenderer->Drain(); + + Timestamp timeStamp; + bool getAudioTime = audioRenderer->GetAudioTime(timeStamp, Timestamp::Timestampbase::MONOTONIC); + EXPECT_EQ(true, getAudioTime); + EXPECT_GE(timeStamp.time.tv_sec, (const long)RendererModuleTestConstants::ZERO); + EXPECT_GE(timeStamp.time.tv_nsec, (const long)RendererModuleTestConstants::ZERO); + + audioRenderer->Release(); + + free(buffer); + fclose(wavFile); +} + +/* + * Feature: Audio render + * Function: Playback + * SubFunction: NA + * FunctionPoints: NA + * EnvConditions: NA + * CaseDescription: Get latency + */ +HWTEST(AudioRendererModuleTest, audio_renderer_get_latency_001, TestSize.Level1) +{ + int32_t ret = 0; + + FILE *wavFile = fopen(RendererModuleTestConstants::AUDIO_TEST_FILE_PATH, "rb"); + EXPECT_NE(nullptr, wavFile); + + unique_ptr audioRenderer = AudioRenderer::Create(AudioStreamType::STREAM_MUSIC); + AudioRendererParams rendererParams; + rendererParams.sampleFormat = AudioSampleFormat::SAMPLE_S16LE; + rendererParams.sampleRate = AudioSamplingRate::SAMPLE_RATE_44100; + rendererParams.channelCount = AudioChannel::STEREO; + rendererParams.encodingType = AudioEncodingType::ENCODING_PCM; + + ret = audioRenderer->SetParams(rendererParams); + EXPECT_EQ(0, ret); + bool isStarted = audioRenderer->Start(); + EXPECT_EQ(true, isStarted); + size_t bufferLen; + ret = audioRenderer->GetBufferSize(bufferLen); + EXPECT_EQ(0, ret); + + uint8_t *buffer = nullptr; + int32_t n = 2; + buffer = (uint8_t *) malloc(n * bufferLen); + size_t bytesToWrite = 0; + size_t bytesWritten = 0; + size_t minBytes = 4; + size_t tempBytesWritten = 0; + size_t totalBytesWritten = 0; + while (!feof(wavFile)) { + bytesToWrite = fread(buffer, 1, bufferLen, wavFile); + bytesWritten = 0; + uint64_t latency; + ret = audioRenderer->GetLatency(latency); + EXPECT_EQ(0, ret); + while ((bytesWritten < bytesToWrite) && ((bytesToWrite - bytesWritten) > minBytes)) { + tempBytesWritten = audioRenderer->Write(buffer + bytesWritten, + bytesToWrite - bytesWritten); + EXPECT_GE(tempBytesWritten, RendererModuleTestConstants::ZERO); + bytesWritten += tempBytesWritten; + } + totalBytesWritten += bytesWritten; + if (totalBytesWritten >= RendererModuleTestConstants::WRITE_SIZE_LIMIT) { + break; + } + } + + audioRenderer->Drain(); + audioRenderer->Release(); + + free(buffer); + fclose(wavFile); +} + + +/* + * Feature: Audio render + * Function: Playback + * SubFunction: NA + * FunctionPoints: NA + * EnvConditions: NA + * CaseDescription: Stop playback + */ +HWTEST(AudioRendererModuleTest, audio_renderer_stop_001, TestSize.Level1) +{ + int32_t ret = 0; + + FILE *wavFile = fopen(RendererModuleTestConstants::AUDIO_TEST_FILE_PATH, "rb"); + EXPECT_NE(nullptr, wavFile); + + unique_ptr audioRenderer = AudioRenderer::Create(AudioStreamType::STREAM_MUSIC); + AudioRendererParams rendererParams; + rendererParams.sampleFormat = AudioSampleFormat::SAMPLE_S16LE; + rendererParams.sampleRate = AudioSamplingRate::SAMPLE_RATE_44100; + rendererParams.channelCount = AudioChannel::STEREO; + rendererParams.encodingType = AudioEncodingType::ENCODING_PCM; + + ret = audioRenderer->SetParams(rendererParams); + EXPECT_EQ(0, ret); + bool isStarted = audioRenderer->Start(); + EXPECT_EQ(true, isStarted); + size_t bufferLen; + ret = audioRenderer->GetBufferSize(bufferLen); + EXPECT_EQ(0, ret); + + uint8_t *buffer = nullptr; + int32_t n = 2; + buffer = (uint8_t *) malloc(n * bufferLen); + size_t bytesToWrite = 0; + size_t bytesWritten = 0; + size_t minBytes = 4; + size_t tempBytesWritten = 0; + size_t totalBytesWritten = 0; + while (!feof(wavFile)) { + bytesToWrite = fread(buffer, 1, bufferLen, wavFile); + bytesWritten = 0; + while ((bytesWritten < bytesToWrite) && ((bytesToWrite - bytesWritten) > minBytes)) { + tempBytesWritten = audioRenderer->Write(buffer + bytesWritten, + bytesToWrite - bytesWritten); + EXPECT_GE(tempBytesWritten, RendererModuleTestConstants::ZERO); + bytesWritten += tempBytesWritten; + } + totalBytesWritten += bytesWritten; + if (totalBytesWritten >= RendererModuleTestConstants::WRITE_SIZE_LIMIT) { + break; + } + } + + bool isStopped = audioRenderer->Stop(); + EXPECT_EQ(true, isStopped); + + audioRenderer->Drain(); + audioRenderer->Release(); + + free(buffer); + fclose(wavFile); +} + +/* + * Feature: Audio render + * Function: Playback + * SubFunction: NA + * FunctionPoints: NA + * EnvConditions: NA + * CaseDescription: Check states + */ +HWTEST(AudioRendererModuleTest, audio_renderer_check_status_001, TestSize.Level1) +{ + int32_t ret = 0; + + FILE *wavFile = fopen(RendererModuleTestConstants::AUDIO_TEST_FILE_PATH, "rb"); + EXPECT_NE(nullptr, wavFile); + + unique_ptr audioRenderer = AudioRenderer::Create(AudioStreamType::STREAM_MUSIC); + RendererState state = audioRenderer->GetStatus(); + EXPECT_EQ(RendererState::RENDERER_NEW, state); + + AudioRendererParams rendererParams; + rendererParams.sampleFormat = AudioSampleFormat::SAMPLE_S16LE; + rendererParams.sampleRate = AudioSamplingRate::SAMPLE_RATE_44100; + rendererParams.channelCount = AudioChannel::STEREO; + rendererParams.encodingType = AudioEncodingType::ENCODING_PCM; + + ret = audioRenderer->SetParams(rendererParams); + EXPECT_EQ(0, ret); + state = audioRenderer->GetStatus(); + EXPECT_EQ(RendererState::RENDERER_PREPARED, state); + + bool isStarted = audioRenderer->Start(); + EXPECT_EQ(true, isStarted); + state = audioRenderer->GetStatus(); + EXPECT_EQ(RendererState::RENDERER_RUNNING, state); + + bool isStopped = audioRenderer->Stop(); + EXPECT_EQ(true, isStopped); + state = audioRenderer->GetStatus(); + EXPECT_EQ(RendererState::RENDERER_STOPPED, state); + + bool isReleased = audioRenderer->Release(); + EXPECT_EQ(true, isReleased); + state = audioRenderer->GetStatus(); + EXPECT_EQ(RendererState::RENDERER_RELEASED, state); +} \ No newline at end of file diff --git a/frameworks/kitsimpl/audio_manager/src/audio_device_descriptor_napi.cpp b/frameworks/kitsimpl/audio_manager/src/audio_device_descriptor_napi.cpp old mode 100755 new mode 100644 index 4ef41de62eb3c7521efdaa132065b1e34e868f87..2ee0823282dc87fe2e62b4803361507d52210e30 --- a/frameworks/kitsimpl/audio_manager/src/audio_device_descriptor_napi.cpp +++ b/frameworks/kitsimpl/audio_manager/src/audio_device_descriptor_napi.cpp @@ -14,13 +14,15 @@ */ #include "audio_device_descriptor_napi.h" -#include "audio_svc_manager.h" +#include "audio_system_manager.h" #include "hilog/log.h" -using namespace OHOS; +using namespace std; using OHOS::HiviewDFX::HiLog; using OHOS::HiviewDFX::HiLogLabel; +namespace OHOS { +namespace AudioStandard { napi_ref AudioDeviceDescriptorNapi::sConstructor_ = nullptr; sptr AudioDeviceDescriptorNapi::sAudioDescriptor_ = nullptr; @@ -42,9 +44,9 @@ AudioDeviceDescriptorNapi::~AudioDeviceDescriptorNapi() void AudioDeviceDescriptorNapi::Destructor(napi_env env, void *nativeObject, void *finalize_hint) { - HiLog::Debug(LABEL, "Destructor() is called!"); if (nativeObject != nullptr) { - reinterpret_cast(nativeObject)->~AudioDeviceDescriptorNapi(); + auto obj = static_cast(nativeObject); + delete obj; } } @@ -52,33 +54,28 @@ static AudioDeviceDescriptorNapi::DeviceType GetJSDeviceType(AudioDeviceDescript { AudioDeviceDescriptorNapi::DeviceType result; - HiLog::Debug(LABEL, "GetJSDeviceType() is called!, %{public}d", deviceType); switch (deviceType) { case AudioDeviceDescriptor::SPEAKER: result = AudioDeviceDescriptorNapi::SPEAKER; break; - case AudioDeviceDescriptor::WIRED_HEADSET: result = AudioDeviceDescriptorNapi::WIRED_HEADSET; break; - case AudioDeviceDescriptor::BLUETOOTH_SCO: result = AudioDeviceDescriptorNapi::BLUETOOTH_SCO; break; - case AudioDeviceDescriptor::BLUETOOTH_A2DP: result = AudioDeviceDescriptorNapi::BLUETOOTH_A2DP; break; - case AudioDeviceDescriptor::MIC: result = AudioDeviceDescriptorNapi::MIC; break; - default: result = AudioDeviceDescriptorNapi::INVALID; HiLog::Error(LABEL, "Unknown device type!"); break; } + return result; } @@ -86,21 +83,19 @@ static AudioDeviceDescriptorNapi::DeviceRole GetJSDeviceRole(AudioDeviceDescript { AudioDeviceDescriptorNapi::DeviceRole result; - HiLog::Debug(LABEL, "GetJSDeviceRole() is called!, %{public}d", deviceRole); switch (deviceRole) { case AudioDeviceDescriptor::INPUT_DEVICE: result = AudioDeviceDescriptorNapi::INPUT_DEVICE; break; - case AudioDeviceDescriptor::OUTPUT_DEVICE: result = AudioDeviceDescriptorNapi::OUTPUT_DEVICE; break; - default: result = AudioDeviceDescriptorNapi::INPUT_DEVICE; HiLog::Error(LABEL, "Unknown device role!"); break; } + return result; } @@ -114,7 +109,6 @@ napi_value AudioDeviceDescriptorNapi::Init(napi_env env, napi_value exports) }; const int32_t refCount = 1; - HiLog::Debug(LABEL, "AudioDeviceDescriptorNapi::Init is called!"); status = napi_define_class(env, AUDIO_DEVICE_DESCRIPTOR_NAPI_CLASS_NAME.c_str(), NAPI_AUTO_LENGTH, Construct, nullptr, sizeof(audio_dev_desc_properties) / sizeof(audio_dev_desc_properties[0]), audio_dev_desc_properties, &constructor); @@ -124,12 +118,12 @@ napi_value AudioDeviceDescriptorNapi::Init(napi_env env, napi_value exports) status = napi_set_named_property(env, exports, AUDIO_DEVICE_DESCRIPTOR_NAPI_CLASS_NAME.c_str(), constructor); if (status == napi_ok) { - HiLog::Info(LABEL, "All props and functions are configured.."); return exports; } } } HiLog::Error(LABEL, "Failure in AudioDeviceDescriptorNapi::Init()"); + return exports; } @@ -139,72 +133,71 @@ napi_value AudioDeviceDescriptorNapi::Construct(napi_env env, napi_callback_info napi_value jsThis = nullptr; size_t argCount = 0; - HiLog::Debug(LABEL, "AudioDeviceDescriptorNapi::Construct() is called!"); status = napi_get_cb_info(env, info, &argCount, nullptr, &jsThis, nullptr); if (status == napi_ok) { - AudioDeviceDescriptorNapi* obj = new AudioDeviceDescriptorNapi(); + unique_ptr obj = make_unique(); if (obj != nullptr) { obj->env_ = env; obj->audioDescriptor_ = sAudioDescriptor_; - status = napi_wrap(env, jsThis, reinterpret_cast(obj), + status = napi_wrap(env, jsThis, static_cast(obj.get()), AudioDeviceDescriptorNapi::Destructor, nullptr, &(obj->wrapper_)); if (status == napi_ok) { - HiLog::Debug(LABEL, "wrapping is succesful in AudioDeviceDescriptorNapi::Construct()"); + obj.release(); return jsThis; } } } HiLog::Error(LABEL, "Failed in AudioDeviceDescriptorNapi::Construct()!"); napi_get_undefined(env, &jsThis); + return jsThis; } napi_value AudioDeviceDescriptorNapi::CreateAudioDeviceDescriptorWrapper(napi_env env, - sptr audioDeviceDescriptor) + sptr deviceDescriptor) { napi_status status; napi_value result = nullptr; napi_value constructor; - HiLog::Debug(LABEL, "CreateAudioDeviceDescriptorWrapper() is called!"); - status = napi_get_reference_value(env, sConstructor_, &constructor); - if (status == napi_ok) { - sAudioDescriptor_ = audioDeviceDescriptor; - status = napi_new_instance(env, constructor, 0, nullptr, &result); - sAudioDescriptor_ = nullptr; + if (deviceDescriptor != nullptr) { + status = napi_get_reference_value(env, sConstructor_, &constructor); if (status == napi_ok) { - return result; + sAudioDescriptor_ = deviceDescriptor; + status = napi_new_instance(env, constructor, 0, nullptr, &result); + sAudioDescriptor_ = nullptr; + if (status == napi_ok) { + return result; + } } + HiLog::Error(LABEL, "Failed in CreateAudioDeviceDescriptorWrapper, %{public}d", status); + } else { + HiLog::Error(LABEL, "sptr is null"); } - HiLog::Error(LABEL, "Failed in AudioDeviceDescriptorNapi::CreateAudioDeviceDescriptorWrapper!, %{public}d", status); napi_get_undefined(env, &result); + return result; } napi_value AudioDeviceDescriptorNapi::GetDeviceRole(napi_env env, napi_callback_info info) { napi_status status; - AudioDeviceDescriptorNapi* audioDeviceDescriptor = nullptr; + AudioDeviceDescriptorNapi* deviceDescriptor = nullptr; size_t argc = 0; napi_value thisVar = nullptr; AudioDeviceDescriptor::DeviceRole deviceRole; napi_value jsResult = nullptr; - HiLog::Debug(LABEL, "GetDeviceRole() is called!"); napi_get_cb_info(env, info, &argc, nullptr, &thisVar, nullptr); NAPI_ASSERT(env, argc == 0, "Invalid number of arguments"); - status = napi_unwrap(env, thisVar, (void **)&audioDeviceDescriptor); + status = napi_unwrap(env, thisVar, (void **)&deviceDescriptor); if (status == napi_ok) { - HiLog::Debug(LABEL, "Unwrapping is succesful in GetDeviceRole()!"); - deviceRole = audioDeviceDescriptor->audioDescriptor_->deviceRole_; - HiLog::Debug(LABEL, "deviceRole: %{public}d", deviceRole); + deviceRole = deviceDescriptor->audioDescriptor_->deviceRole_; status = napi_create_int32(env, GetJSDeviceRole(deviceRole), &jsResult); if (status == napi_ok) { - HiLog::Debug(LABEL, "returning device role!"); return jsResult; } } - HiLog::Debug(LABEL, "returning undefine from GetDeviceRole()!"); napi_get_undefined(env, &jsResult); return jsResult; } @@ -216,23 +209,20 @@ napi_value AudioDeviceDescriptorNapi::GetDeviceType(napi_env env, napi_callback_ napi_value thisVar = nullptr; AudioDeviceDescriptor::DeviceType deviceType; napi_value jsResult = nullptr; - AudioDeviceDescriptorNapi* audioDeviceDescriptor = nullptr; + AudioDeviceDescriptorNapi* deviceDescriptor = nullptr; - HiLog::Debug(LABEL, "GetDeviceType() is called!"); napi_get_cb_info(env, info, &argc, nullptr, &thisVar, nullptr); NAPI_ASSERT(env, argc == 0, "Invalid number of arguments"); - status = napi_unwrap(env, thisVar, (void **)&audioDeviceDescriptor); + status = napi_unwrap(env, thisVar, (void **)&deviceDescriptor); if (status == napi_ok) { - HiLog::Debug(LABEL, "Unwrapping is succesful in GetDeviceType()!"); - deviceType = audioDeviceDescriptor->audioDescriptor_->deviceType_; - HiLog::Debug(LABEL, "deviceType: %{public}d", deviceType); + deviceType = deviceDescriptor->audioDescriptor_->deviceType_; status = napi_create_int32(env, GetJSDeviceType(deviceType), &jsResult); if (status == napi_ok) { - HiLog::Debug(LABEL, "returning device type!"); return jsResult; } } - HiLog::Debug(LABEL, "returning undefined from GetDeviceType()!"); napi_get_undefined(env, &jsResult); return jsResult; } +} // namespace AudioStandard +} // namespace OHOS diff --git a/frameworks/kitsimpl/audio_manager/src/audio_manager_napi.cpp b/frameworks/kitsimpl/audio_manager/src/audio_manager_napi.cpp old mode 100755 new mode 100644 index 241b359cecfdb9a76ef942e1e16c368f00cd8469..f7c95f6c536c650907742980aef88dd38f12050d --- a/frameworks/kitsimpl/audio_manager/src/audio_manager_napi.cpp +++ b/frameworks/kitsimpl/audio_manager/src/audio_manager_napi.cpp @@ -17,15 +17,18 @@ #include "audio_device_descriptor_napi.h" #include "hilog/log.h" -using namespace OHOS; +using namespace std; using OHOS::HiviewDFX::HiLog; using OHOS::HiviewDFX::HiLogLabel; +namespace OHOS { +namespace AudioStandard { napi_ref AudioManagerNapi::sConstructor_ = nullptr; napi_ref AudioManagerNapi::audioVolumeTypeRef_ = nullptr; napi_ref AudioManagerNapi::deviceFlagRef_ = nullptr; napi_ref AudioManagerNapi::deviceRoleRef_ = nullptr; napi_ref AudioManagerNapi::deviceTypeRef_ = nullptr; +napi_ref AudioManagerNapi::audioRingModeRef_ = nullptr; #define GET_PARAMS(env, info, num) \ size_t argc = num; \ @@ -40,11 +43,20 @@ struct AudioManagerAsyncContext { napi_deferred deferred; napi_ref callbackRef = nullptr; int32_t volType; - int32_t volLevel; + int32_t deviceType; + int32_t ringMode; int32_t deviceFlag; - int status; + int32_t intValue; + int32_t status; + bool isMute; + bool isActive; + bool isTrue; + double volLevel; + double doubleValue; + string key; + string valueStr; AudioManagerNapi* objectInfo; - std::vector> deviceDescriptors; + vector> deviceDescriptors; }; namespace { @@ -70,65 +82,141 @@ AudioManagerNapi::~AudioManagerNapi() void AudioManagerNapi::Destructor(napi_env env, void *nativeObject, void *finalize_hint) { if (nativeObject != nullptr) { - reinterpret_cast(nativeObject)->~AudioManagerNapi(); + auto obj = static_cast(nativeObject); + delete obj; } } -static AudioSvcManager::AudioVolumeType GetNativeAudioVolumeType(int32_t volumeType) +static AudioSystemManager::AudioVolumeType GetNativeAudioVolumeType(int32_t volumeType) { - AudioSvcManager::AudioVolumeType result; + AudioSystemManager::AudioVolumeType result = AudioSystemManager::STREAM_MUSIC; switch (volumeType) { + case AudioManagerNapi::RINGTONE: + result = AudioSystemManager::STREAM_RING; + break; case AudioManagerNapi::MEDIA: - result = AudioSvcManager::STREAM_MUSIC; + result = AudioSystemManager::STREAM_MUSIC; break; - - case AudioManagerNapi::RINGTONE: - result = AudioSvcManager::STREAM_RING; + default: + result = AudioSystemManager::STREAM_MUSIC; + HiLog::Error(LABEL, "Unknown volume type, Set it to default MEDIA!"); break; + } + + return result; +} + +static AudioDeviceDescriptor::DeviceType GetNativeDeviceType(int32_t deviceType) +{ + AudioDeviceDescriptor::DeviceType result = AudioDeviceDescriptor::DEVICE_TYPE_NONE; + switch (deviceType) { + case AudioDeviceDescriptorNapi::SPEAKER: + result = AudioDeviceDescriptor::SPEAKER; + break; + case AudioDeviceDescriptorNapi::WIRED_HEADSET: + result = AudioDeviceDescriptor::WIRED_HEADSET; + break; + case AudioDeviceDescriptorNapi::BLUETOOTH_SCO: + result = AudioDeviceDescriptor::BLUETOOTH_SCO; + break; + case AudioDeviceDescriptorNapi::BLUETOOTH_A2DP: + result = AudioDeviceDescriptor::BLUETOOTH_A2DP; + break; + case AudioDeviceDescriptorNapi::MIC: + result = AudioDeviceDescriptor::MIC; + break; + case AudioDeviceDescriptorNapi::INVALID: default: - result = AudioSvcManager::STREAM_MUSIC; - HiLog::Error(LABEL, "Unknown volume type!, %{public}d", volumeType); + result = AudioDeviceDescriptor::DEVICE_TYPE_NONE; + HiLog::Error(LABEL, "Unknown device type!"); break; } + return result; } static AudioDeviceDescriptor::DeviceFlag GetNativeDeviceFlag(int32_t deviceFlag) { - AudioDeviceDescriptor::DeviceFlag result; + AudioDeviceDescriptor::DeviceFlag result = AudioDeviceDescriptor::ALL_DEVICES_FLAG; switch (deviceFlag) { case AudioManagerNapi::OUTPUT_DEVICES_FLAG: result = AudioDeviceDescriptor::OUTPUT_DEVICES_FLAG; break; - case AudioManagerNapi::INPUT_DEVICES_FLAG: result = AudioDeviceDescriptor::INPUT_DEVICES_FLAG; break; - case AudioManagerNapi::ALL_DEVICES_FLAG: result = AudioDeviceDescriptor::ALL_DEVICES_FLAG; break; - default: result = AudioDeviceDescriptor::ALL_DEVICES_FLAG; HiLog::Error(LABEL, "Unknown device flag!, %{public}d", deviceFlag); break; } + + return result; +} + + +static AudioRingerMode GetNativeAudioRingerMode(int32_t ringMode) +{ + AudioRingerMode result = RINGER_MODE_NORMAL; + + switch (ringMode) { + case AudioManagerNapi::RINGER_MODE_SILENT: + result = RINGER_MODE_SILENT; + break; + case AudioManagerNapi::RINGER_MODE_VIBRATE: + result = RINGER_MODE_VIBRATE; + break; + case AudioManagerNapi::RINGER_MODE_NORMAL: + result = RINGER_MODE_NORMAL; + break; + default: + result = RINGER_MODE_NORMAL; + HiLog::Error(LABEL, "Unknown ringer mode requested by JS, Set it to default RINGER_MODE_NORMAL!"); + break; + } + + return result; +} + +static AudioManagerNapi::AudioRingMode GetJsAudioRingMode(int32_t ringerMode) +{ + AudioManagerNapi::AudioRingMode result = AudioManagerNapi::RINGER_MODE_NORMAL; + + switch (ringerMode) { + case RINGER_MODE_SILENT: + result = AudioManagerNapi::RINGER_MODE_SILENT; + break; + case RINGER_MODE_VIBRATE: + result = AudioManagerNapi::RINGER_MODE_VIBRATE; + break; + case RINGER_MODE_NORMAL: + result = AudioManagerNapi::RINGER_MODE_NORMAL; + break; + default: + result = AudioManagerNapi::RINGER_MODE_NORMAL; + HiLog::Error(LABEL, "Unknown ringer mode returned from native, Set it to default RINGER_MODE_NORMAL!"); + break; + } + return result; } -napi_status AudioManagerNapi::AddNamedProperty(napi_env env, napi_value object, const char *name, int32_t enumValue) +napi_status AudioManagerNapi::AddNamedProperty(napi_env env, napi_value object, const string name, int32_t enumValue) { napi_status status; napi_value enumNapiValue; status = napi_create_int32(env, enumValue, &enumNapiValue); if (status == napi_ok) { - status = napi_set_named_property(env, object, name, enumNapiValue); + status = napi_set_named_property(env, object, name.c_str(), enumNapiValue); } + return status; } @@ -137,23 +225,39 @@ napi_value AudioManagerNapi::CreateAudioVolumeTypeObject(napi_env env) napi_value result = nullptr; napi_status status; int32_t refCount = 1; + string propName; - HiLog::Debug(LABEL, "CreateAudioVolumeTypeObject is called!"); status = napi_create_object(env, &result); if (status == napi_ok) { - status = AddNamedProperty(env, result, "MEDIA", AudioManagerNapi::MEDIA); + for (int i = AudioManagerNapi::RINGTONE; i <= AudioManagerNapi::MEDIA; i++) { + switch (i) { + case AudioManagerNapi::RINGTONE: + propName = "RINGTONE"; + break; + case AudioManagerNapi::MEDIA: + propName = "MEDIA"; + break; + default: + HiLog::Error(LABEL, "Invalid prop!"); + continue; + } + status = AddNamedProperty(env, result, propName, i); + if (status != napi_ok) { + HiLog::Error(LABEL, "Failed to add named prop!"); + break; + } + propName.clear(); + } if (status == napi_ok) { - status = AddNamedProperty(env, result, "RINGTONE", AudioManagerNapi::RINGTONE); + status = napi_create_reference(env, result, refCount, &audioVolumeTypeRef_); if (status == napi_ok) { - status = napi_create_reference(env, result, refCount, &audioVolumeTypeRef_); - if (status == napi_ok) { - return result; - } + return result; } } } HiLog::Error(LABEL, "CreateAudioVolumeTypeObject is Failed!"); napi_get_undefined(env, &result); + return result; } @@ -162,26 +266,42 @@ napi_value AudioManagerNapi::CreateDeviceFlagObject(napi_env env) napi_value result = nullptr; napi_status status; int32_t refCount = 1; + string propName; - HiLog::Debug(LABEL, "CreateDeviceFlagObject is called!"); status = napi_create_object(env, &result); if (status == napi_ok) { - status = AddNamedProperty(env, result, "OUTPUT_DEVICES_FLAG", AudioManagerNapi::OUTPUT_DEVICES_FLAG); + for (int i = AudioManagerNapi::OUTPUT_DEVICES_FLAG; i <= AudioManagerNapi::ALL_DEVICES_FLAG; i++) { + switch (i) { + case AudioManagerNapi::OUTPUT_DEVICES_FLAG: + propName = "OUTPUT_DEVICES_FLAG"; + break; + case AudioManagerNapi::INPUT_DEVICES_FLAG: + propName = "INPUT_DEVICES_FLAG"; + break; + case AudioManagerNapi::ALL_DEVICES_FLAG: + propName = "ALL_DEVICES_FLAG"; + break; + default: + HiLog::Error(LABEL, "Invalid prop!"); + continue; + } + status = AddNamedProperty(env, result, propName, i); + if (status != napi_ok) { + HiLog::Error(LABEL, "Failed to add named prop!"); + break; + } + propName.clear(); + } if (status == napi_ok) { - status = AddNamedProperty(env, result, "INPUT_DEVICES_FLAG", AudioManagerNapi::INPUT_DEVICES_FLAG); + status = napi_create_reference(env, result, refCount, &deviceFlagRef_); if (status == napi_ok) { - status = AddNamedProperty(env, result, "ALL_DEVICES_FLAG", AudioManagerNapi::ALL_DEVICES_FLAG); - if (status == napi_ok) { - status = napi_create_reference(env, result, refCount, &deviceFlagRef_); - if (status == napi_ok) { - return result; - } - } + return result; } } } HiLog::Error(LABEL, "CreateDeviceFlagObject is Failed!"); napi_get_undefined(env, &result); + return result; } @@ -190,23 +310,39 @@ napi_value AudioManagerNapi::CreateDeviceRoleObject(napi_env env) napi_value result = nullptr; napi_status status; int32_t refCount = 1; + string propName; - HiLog::Debug(LABEL, "CreateDeviceRoleObject is called!"); status = napi_create_object(env, &result); if (status == napi_ok) { - status = AddNamedProperty(env, result, "INPUT_DEVICE", AudioDeviceDescriptorNapi::INPUT_DEVICE); + for (int i = AudioDeviceDescriptorNapi::INPUT_DEVICE; i <= AudioDeviceDescriptorNapi::OUTPUT_DEVICE; i++) { + switch (i) { + case AudioDeviceDescriptorNapi::INPUT_DEVICE: + propName = "INPUT_DEVICE"; + break; + case AudioDeviceDescriptorNapi::OUTPUT_DEVICE: + propName = "OUTPUT_DEVICE"; + break; + default: + HiLog::Error(LABEL, "Invalid prop!"); + continue; + } + status = AddNamedProperty(env, result, propName, i); + if (status != napi_ok) { + HiLog::Error(LABEL, "Failed to add named prop!"); + break; + } + propName.clear(); + } if (status == napi_ok) { - status = AddNamedProperty(env, result, "OUTPUT_DEVICE", AudioDeviceDescriptorNapi::OUTPUT_DEVICE); + status = napi_create_reference(env, result, refCount, &deviceRoleRef_); if (status == napi_ok) { - status = napi_create_reference(env, result, refCount, &deviceRoleRef_); - if (status == napi_ok) { - return result; - } + return result; } } } HiLog::Error(LABEL, "CreateDeviceRoleObject is Failed!"); napi_get_undefined(env, &result); + return result; } @@ -215,9 +351,8 @@ napi_value AudioManagerNapi::CreateDeviceTypeObject(napi_env env) napi_value result = nullptr; napi_status status; int32_t refCount = 1; - const char *propName = nullptr; + string propName; - HiLog::Debug(LABEL, "CreateDeviceTypeObject is called!"); status = napi_create_object(env, &result); if (status == napi_ok) { for (int i = AudioDeviceDescriptorNapi::INVALID; i <= AudioDeviceDescriptorNapi::MIC; i++) { @@ -225,27 +360,21 @@ napi_value AudioManagerNapi::CreateDeviceTypeObject(napi_env env) case AudioDeviceDescriptorNapi::INVALID: propName = "INVALID"; break; - case AudioDeviceDescriptorNapi::SPEAKER: propName = "SPEAKER"; break; - case AudioDeviceDescriptorNapi::WIRED_HEADSET: propName = "WIRED_HEADSET"; break; - case AudioDeviceDescriptorNapi::BLUETOOTH_SCO: propName = "BLUETOOTH_SCO"; break; - case AudioDeviceDescriptorNapi::BLUETOOTH_A2DP: propName = "BLUETOOTH_A2DP"; break; - case AudioDeviceDescriptorNapi::MIC: propName = "MIC"; break; - default: HiLog::Error(LABEL, "Invalid prop!"); continue; @@ -255,7 +384,7 @@ napi_value AudioManagerNapi::CreateDeviceTypeObject(napi_env env) HiLog::Error(LABEL, "Failed to add named prop!"); break; } - propName = nullptr; + propName.clear(); } if (status == napi_ok) { status = napi_create_reference(env, result, refCount, &deviceTypeRef_); @@ -266,6 +395,51 @@ napi_value AudioManagerNapi::CreateDeviceTypeObject(napi_env env) } HiLog::Error(LABEL, "CreateDeviceTypeObject is Failed!"); napi_get_undefined(env, &result); + + return result; +} + +napi_value AudioManagerNapi::CreateAudioRingModeObject(napi_env env) +{ + napi_value result = nullptr; + napi_status status; + int32_t refCount = 1; + string propName; + + status = napi_create_object(env, &result); + if (status == napi_ok) { + for (int i = AudioManagerNapi::RINGER_MODE_SILENT; i <= AudioManagerNapi::RINGER_MODE_NORMAL; i++) { + switch (i) { + case AudioManagerNapi::RINGER_MODE_SILENT: + propName = "RINGER_MODE_SILENT"; + break; + case AudioManagerNapi::RINGER_MODE_VIBRATE: + propName = "RINGER_MODE_VIBRATE"; + break; + case AudioManagerNapi::RINGER_MODE_NORMAL: + propName = "RINGER_MODE_NORMAL"; + break; + default: + HiLog::Error(LABEL, "Invalid prop!"); + continue; + } + status = AddNamedProperty(env, result, propName, i); + if (status != napi_ok) { + HiLog::Error(LABEL, "Failed to add named prop!"); + break; + } + propName.clear(); + } + if (status == napi_ok) { + status = napi_create_reference(env, result, refCount, &audioRingModeRef_); + if (status == napi_ok) { + return result; + } + } + } + HiLog::Error(LABEL, "CreateAudioRingModeObject is Failed!"); + napi_get_undefined(env, &result); + return result; } @@ -276,13 +450,23 @@ napi_value AudioManagerNapi::Init(napi_env env, napi_value exports) napi_value result = nullptr; const int32_t refCount = 1; - HiLog::Debug(LABEL, "AudioManagerNapi::Init() is called!"); napi_property_descriptor audio_svc_mngr_properties[] = { DECLARE_NAPI_FUNCTION("setVolume", SetVolume), DECLARE_NAPI_FUNCTION("getVolume", GetVolume), DECLARE_NAPI_FUNCTION("getMaxVolume", GetMaxVolume), DECLARE_NAPI_FUNCTION("getMinVolume", GetMinVolume), - DECLARE_NAPI_FUNCTION("getDevices", GetDevices) + DECLARE_NAPI_FUNCTION("getDevices", GetDevices), + DECLARE_NAPI_FUNCTION("mute", SetStreamMute), + DECLARE_NAPI_FUNCTION("isMute", IsStreamMute), + DECLARE_NAPI_FUNCTION("isActive", IsStreamActive), + DECLARE_NAPI_FUNCTION("setRingerMode", SetRingerMode), + DECLARE_NAPI_FUNCTION("getRingerMode", GetRingerMode), + DECLARE_NAPI_FUNCTION("setDeviceActive", SetDeviceActive), + DECLARE_NAPI_FUNCTION("isDeviceActive", IsDeviceActive), + DECLARE_NAPI_FUNCTION("setAudioParameter", SetAudioParameter), + DECLARE_NAPI_FUNCTION("getAudioParameter", GetAudioParameter), + DECLARE_NAPI_FUNCTION("setMicrophoneMute", SetMicrophoneMute), + DECLARE_NAPI_FUNCTION("isMicrophoneMute", IsMicrophoneMute) }; napi_property_descriptor static_prop[] = { @@ -290,7 +474,8 @@ napi_value AudioManagerNapi::Init(napi_env env, napi_value exports) DECLARE_NAPI_PROPERTY("AudioVolumeType", CreateAudioVolumeTypeObject(env)), DECLARE_NAPI_PROPERTY("DeviceFlag", CreateDeviceFlagObject(env)), DECLARE_NAPI_PROPERTY("DeviceRole", CreateDeviceRoleObject(env)), - DECLARE_NAPI_PROPERTY("DeviceType", CreateDeviceTypeObject(env)) + DECLARE_NAPI_PROPERTY("DeviceType", CreateDeviceTypeObject(env)), + DECLARE_NAPI_PROPERTY("AudioRingMode", CreateAudioRingModeObject(env)) }; status = napi_define_class(env, AUDIO_MNGR_NAPI_CLASS_NAME.c_str(), NAPI_AUTO_LENGTH, Construct, nullptr, @@ -304,7 +489,6 @@ napi_value AudioManagerNapi::Init(napi_env env, napi_value exports) status = napi_define_properties(env, exports, sizeof(static_prop) / sizeof(static_prop[PARAM0]), static_prop); if (status == napi_ok) { - HiLog::Info(LABEL, "All props and functions are configured.."); return exports; } } @@ -312,6 +496,7 @@ napi_value AudioManagerNapi::Init(napi_env env, napi_value exports) } HiLog::Error(LABEL, "Failure in AudioManagerNapi::Init()"); napi_get_undefined(env, &result); + return result; } @@ -323,22 +508,23 @@ napi_value AudioManagerNapi::Construct(napi_env env, napi_callback_info info) napi_value result = nullptr; size_t argCount = 0; - HiLog::Debug(LABEL, "AudioManagerNapi::Construct() is called!"); status = napi_get_cb_info(env, info, &argCount, nullptr, &jsThis, nullptr); if (status == napi_ok) { - AudioManagerNapi* obj = new AudioManagerNapi(); + unique_ptr obj = make_unique(); if (obj != nullptr) { obj->env_ = env; - obj->audioMngr_ = AudioSvcManager::GetInstance(); - status = napi_wrap(env, jsThis, reinterpret_cast(obj), + obj->audioMngr_ = AudioSystemManager::GetInstance(); + status = napi_wrap(env, jsThis, static_cast(obj.get()), AudioManagerNapi::Destructor, nullptr, &(obj->wrapper_)); if (status == napi_ok) { + obj.release(); return jsThis; } } } HiLog::Error(LABEL, "Failed in AudioManagerNapi::Construct()!"); napi_get_undefined(env, &result); + return result; } @@ -348,7 +534,6 @@ napi_value AudioManagerNapi::CreateAudioManagerWrapper(napi_env env) napi_value result = nullptr; napi_value constructor; - HiLog::Debug(LABEL, "AudioManagerNapi::CreateAudioManagerWrapper() is called!"); status = napi_get_reference_value(env, sConstructor_, &constructor); if (status == napi_ok) { status = napi_new_instance(env, constructor, 0, nullptr, &result); @@ -358,6 +543,7 @@ napi_value AudioManagerNapi::CreateAudioManagerWrapper(napi_env env) } HiLog::Error(LABEL, "Failed in AudioManagerNapi::CreateaudioMngrWrapper!"); napi_get_undefined(env, &result); + return result; } @@ -366,25 +552,60 @@ napi_value AudioManagerNapi::GetAudioManager(napi_env env, napi_callback_info in napi_status status; size_t argCount = 0; - HiLog::Debug(LABEL, "AudioManagerNapi::GetAudioManager() is called!"); status = napi_get_cb_info(env, info, &argCount, nullptr, nullptr, nullptr); if (status != napi_ok || argCount != 0) { HiLog::Error(LABEL, "Invalid arguments!"); return nullptr; } + return AudioManagerNapi::CreateAudioManagerWrapper(env); } -static void SetVolumeAsyncCallbackComplete(napi_env env, napi_status status, void* data) +// Function to read string argument from napi_value +static string GetStringArgument(napi_env env, napi_value value) +{ + napi_status status; + string strValue = ""; + size_t bufLength = 0; + char *buffer = nullptr; + + status = napi_get_value_string_utf8(env, value, nullptr, 0, &bufLength); + if (status == napi_ok && bufLength > 0) { + buffer = (char *) malloc((bufLength + 1) * sizeof(char)); + if (buffer != nullptr) { + status = napi_get_value_string_utf8(env, value, buffer, bufLength + 1, &bufLength); + if (status == napi_ok) { + strValue = buffer; + } + free(buffer); + buffer = nullptr; + } + } + + return strValue; +} + +static void CommonCallbackRoutine(napi_env env, AudioManagerAsyncContext* &asyncContext, const napi_value &valueParam) { - AudioManagerAsyncContext* asyncContext = (AudioManagerAsyncContext*) data; napi_value result[ARGS_TWO] = {0}; napi_value retVal; - napi_get_undefined(env, &result[PARAM0]); - napi_get_undefined(env, &result[PARAM1]); + + if (!asyncContext->status) { + napi_get_undefined(env, &result[PARAM0]); + result[PARAM1] = valueParam; + } else { + napi_value message = nullptr; + napi_create_string_utf8(env, "Error, Operation not supported or Failed", NAPI_AUTO_LENGTH, &message); + napi_create_error(env, nullptr, message, &result[PARAM0]); + napi_get_undefined(env, &result[PARAM1]); + } if (asyncContext->deferred) { - napi_resolve_deferred(env, asyncContext->deferred, result[PARAM1]); + if (!asyncContext->status) { + napi_resolve_deferred(env, asyncContext->deferred, result[PARAM1]); + } else { + napi_reject_deferred(env, asyncContext->deferred, result[PARAM0]); + } } else { napi_value callback = nullptr; napi_get_reference_value(env, asyncContext->callbackRef, &callback); @@ -392,114 +613,108 @@ static void SetVolumeAsyncCallbackComplete(napi_env env, napi_status status, voi napi_delete_reference(env, asyncContext->callbackRef); } napi_delete_async_work(env, asyncContext->work); + delete asyncContext; } -napi_value AudioManagerNapi::SetVolume(napi_env env, napi_callback_info info) +static void SetFunctionAsyncCallbackComplete(napi_env env, napi_status status, void* data) { - napi_status status; - const int32_t refCount = 1; - napi_value result = nullptr; + auto asyncContext = static_cast(data); + napi_value valueParam = nullptr; - GET_PARAMS(env, info, ARGS_THREE); - NAPI_ASSERT(env, argc >= ARGS_TWO, "requires 2 parameters minimum"); + if (asyncContext != nullptr) { + if (!asyncContext->status) { + napi_get_undefined(env, &valueParam); + } + CommonCallbackRoutine(env, asyncContext, valueParam); + } else { + HiLog::Error(LABEL, "ERROR: AudioManagerAsyncContext* is Null!"); + } +} - HiLog::Debug(LABEL, "AudioManagerNapi::SetVolume() is called!"); - AudioManagerAsyncContext* asyncContext = new AudioManagerAsyncContext(); - status = napi_unwrap(env, thisVar, reinterpret_cast(&asyncContext->objectInfo)); - if (status == napi_ok && asyncContext->objectInfo != nullptr) { - for (size_t i = PARAM0; i < argc; i++) { - napi_valuetype valueType = napi_undefined; - napi_typeof(env, argv[i], &valueType); +static void IsTrueAsyncCallbackComplete(napi_env env, napi_status status, void* data) +{ + auto asyncContext = static_cast(data); + napi_value valueParam = nullptr; - if (i == PARAM0 && valueType == napi_number) { - napi_get_value_int32(env, argv[i], &asyncContext->volType); - } else if (i == PARAM1 && valueType == napi_number) { - napi_get_value_int32(env, argv[i], &asyncContext->volLevel); - } else if (i == PARAM2 && valueType == napi_function) { - napi_create_reference(env, argv[i], refCount, &asyncContext->callbackRef); - break; - } else { - delete asyncContext; - NAPI_ASSERT(env, false, "type mismatch"); - } + if (asyncContext != nullptr) { + if (!asyncContext->status) { + napi_get_boolean(env, asyncContext->isTrue, &valueParam); } + CommonCallbackRoutine(env, asyncContext, valueParam); + } else { + HiLog::Error(LABEL, "ERROR: AudioManagerAsyncContext* is Null!"); + } +} - if (asyncContext->callbackRef == nullptr) { - napi_create_promise(env, &asyncContext->deferred, &result); - } else { - napi_get_undefined(env, &result); +static void GetDoubleValueAsyncCallbackComplete(napi_env env, napi_status status, void* data) +{ + auto asyncContext = static_cast(data); + napi_value valueParam = nullptr; + + if (asyncContext != nullptr) { + if (!asyncContext->status) { + napi_create_double(env, asyncContext->doubleValue, &valueParam); } + CommonCallbackRoutine(env, asyncContext, valueParam); + } else { + HiLog::Error(LABEL, "ERROR: AudioManagerAsyncContext* is Null!"); + } +} - napi_value resource = nullptr; - napi_create_string_utf8(env, "SetVolume", NAPI_AUTO_LENGTH, &resource); +static void GetStringValueAsyncCallbackComplete(napi_env env, napi_status status, void* data) +{ + auto asyncContext = static_cast(data); + napi_value valueParam = nullptr; - status = napi_create_async_work( - env, nullptr, resource, - [](napi_env env, void* data) { - AudioManagerAsyncContext* context = (AudioManagerAsyncContext*) data; - context->objectInfo->audioMngr_->SetVolume(GetNativeAudioVolumeType(context->volType), - context->volLevel); - context->status = 0; - }, - SetVolumeAsyncCallbackComplete, (void*)asyncContext, &asyncContext->work); - if (status != napi_ok) { - delete asyncContext; - result = nullptr; - } else { - napi_queue_async_work(env, asyncContext->work); + if (asyncContext != nullptr) { + if (!asyncContext->status) { + napi_create_string_utf8(env, asyncContext->valueStr.c_str(), NAPI_AUTO_LENGTH, &valueParam); } + CommonCallbackRoutine(env, asyncContext, valueParam); } else { - delete asyncContext; + HiLog::Error(LABEL, "ERROR: AudioManagerAsyncContext* is Null!"); } - return result; } -static void GetVolumeAsyncCallbackComplete(napi_env env, napi_status status, void* data) +static void GetIntValueAsyncCallbackComplete(napi_env env, napi_status status, void* data) { - AudioManagerAsyncContext* asyncContext = (AudioManagerAsyncContext*) data; - napi_value result[ARGS_TWO] = {0}; - napi_value retVal; - - napi_get_undefined(env, &result[PARAM0]); - napi_create_int32(env, asyncContext->volLevel, &result[PARAM1]); + auto asyncContext = static_cast(data); + napi_value valueParam = nullptr; - if (asyncContext->deferred) { - napi_resolve_deferred(env, asyncContext->deferred, result[PARAM1]); + if (asyncContext != nullptr) { + if (!asyncContext->status) { + napi_create_int32(env, asyncContext->intValue, &valueParam); + } + CommonCallbackRoutine(env, asyncContext, valueParam); } else { - napi_value callback = nullptr; - napi_get_reference_value(env, asyncContext->callbackRef, &callback); - napi_call_function(env, nullptr, callback, ARGS_TWO, result, &retVal); - napi_delete_reference(env, asyncContext->callbackRef); + HiLog::Error(LABEL, "ERROR: AudioManagerAsyncContext* is Null!"); } - napi_delete_async_work(env, asyncContext->work); - delete asyncContext; } -napi_value AudioManagerNapi::GetVolume(napi_env env, napi_callback_info info) +napi_value AudioManagerNapi::SetMicrophoneMute(napi_env env, napi_callback_info info) { napi_status status; const int32_t refCount = 1; napi_value result = nullptr; GET_PARAMS(env, info, ARGS_TWO); - NAPI_ASSERT(env, argc >= ARGS_ONE, "requires 1 parameters minimum"); + NAPI_ASSERT(env, argc >= ARGS_ONE, "requires 1 parameter minimum"); + + unique_ptr asyncContext = make_unique(); - HiLog::Debug(LABEL, "AudioManagerNapi::GetVolume() is called!"); - AudioManagerAsyncContext* asyncContext = new AudioManagerAsyncContext(); status = napi_unwrap(env, thisVar, reinterpret_cast(&asyncContext->objectInfo)); if (status == napi_ok && asyncContext->objectInfo != nullptr) { for (size_t i = PARAM0; i < argc; i++) { napi_valuetype valueType = napi_undefined; napi_typeof(env, argv[i], &valueType); - if (i == PARAM0 && valueType == napi_number) { - napi_get_value_int32(env, argv[i], &asyncContext->volType); + if (i == PARAM0 && valueType == napi_boolean) { + napi_get_value_bool(env, argv[i], &asyncContext->isMute); } else if (i == PARAM1 && valueType == napi_function) { napi_create_reference(env, argv[i], refCount, &asyncContext->callbackRef); break; } else { - delete asyncContext; NAPI_ASSERT(env, false, "type mismatch"); } } @@ -511,74 +726,50 @@ napi_value AudioManagerNapi::GetVolume(napi_env env, napi_callback_info info) } napi_value resource = nullptr; - napi_create_string_utf8(env, "GetVolume", NAPI_AUTO_LENGTH, &resource); + napi_create_string_utf8(env, "SetMicrophoneMute", NAPI_AUTO_LENGTH, &resource); status = napi_create_async_work( env, nullptr, resource, [](napi_env env, void* data) { - AudioManagerAsyncContext* context = (AudioManagerAsyncContext*) data; - context->volLevel = - context->objectInfo->audioMngr_->GetVolume(GetNativeAudioVolumeType(context->volType)); - context->status = 0; + auto context = static_cast(data); + context->status = context->objectInfo->audioMngr_->SetMicrophoneMute(context->isMute); }, - GetVolumeAsyncCallbackComplete, (void*)asyncContext, &asyncContext->work); + SetFunctionAsyncCallbackComplete, static_cast(asyncContext.get()), &asyncContext->work); if (status != napi_ok) { - delete asyncContext; result = nullptr; } else { - napi_queue_async_work(env, asyncContext->work); + status = napi_queue_async_work(env, asyncContext->work); + if (status == napi_ok) { + asyncContext.release(); + } else { + result = nullptr; + } } - } else { - delete asyncContext; } - return result; -} - -static void GetMaxVolumeAsyncCallbackComplete(napi_env env, napi_status status, void* data) -{ - AudioManagerAsyncContext* asyncContext = (AudioManagerAsyncContext*) data; - napi_value result[ARGS_TWO] = {0}; - napi_value retVal; - - napi_get_undefined(env, &result[PARAM0]); - napi_create_int32(env, asyncContext->volLevel, &result[PARAM1]); - if (asyncContext->deferred) { - napi_resolve_deferred(env, asyncContext->deferred, result[PARAM1]); - } else { - napi_value callback = nullptr; - napi_get_reference_value(env, asyncContext->callbackRef, &callback); - napi_call_function(env, nullptr, callback, ARGS_TWO, result, &retVal); - napi_delete_reference(env, asyncContext->callbackRef); - } - napi_delete_async_work(env, asyncContext->work); - delete asyncContext; + return result; } -napi_value AudioManagerNapi::GetMaxVolume(napi_env env, napi_callback_info info) +napi_value AudioManagerNapi::IsMicrophoneMute(napi_env env, napi_callback_info info) { napi_status status; const int32_t refCount = 1; napi_value result = nullptr; - GET_PARAMS(env, info, ARGS_TWO); - NAPI_ASSERT(env, argc >= ARGS_ONE, "requires 1 parameters minimum"); + GET_PARAMS(env, info, ARGS_ONE); + + unique_ptr asyncContext = make_unique(); - HiLog::Debug(LABEL, "AudioManagerNapi::GetMaxVolume() is called!"); - AudioManagerAsyncContext* asyncContext = new AudioManagerAsyncContext(); status = napi_unwrap(env, thisVar, reinterpret_cast(&asyncContext->objectInfo)); if (status == napi_ok && asyncContext->objectInfo != nullptr) { for (size_t i = PARAM0; i < argc; i++) { napi_valuetype valueType = napi_undefined; napi_typeof(env, argv[i], &valueType); - if (i == PARAM0 && valueType == napi_number) { - napi_get_value_int32(env, argv[i], &asyncContext->volType); - } else if (i == PARAM1 && valueType == napi_function) { + if (i == PARAM0 && valueType == napi_function) { napi_create_reference(env, argv[i], refCount, &asyncContext->callbackRef); break; } else { - delete asyncContext; NAPI_ASSERT(env, false, "type mismatch"); } } @@ -590,48 +781,752 @@ napi_value AudioManagerNapi::GetMaxVolume(napi_env env, napi_callback_info info) } napi_value resource = nullptr; - napi_create_string_utf8(env, "GetMaxVolume", NAPI_AUTO_LENGTH, &resource); + napi_create_string_utf8(env, "IsMicrophoneMute", NAPI_AUTO_LENGTH, &resource); + + status = napi_create_async_work( + env, nullptr, resource, + [](napi_env env, void* data) { + auto context = static_cast(data); + context->isMute = context->objectInfo->audioMngr_->IsMicrophoneMute(); + context->isTrue = context->isMute; + }, + IsTrueAsyncCallbackComplete, static_cast(asyncContext.get()), &asyncContext->work); + if (status != napi_ok) { + result = nullptr; + } else { + status = napi_queue_async_work(env, asyncContext->work); + if (status == napi_ok) { + asyncContext.release(); + } else { + result = nullptr; + } + } + } + + return result; +} + +napi_value AudioManagerNapi::SetRingerMode(napi_env env, napi_callback_info info) +{ + napi_status status; + const int32_t refCount = 1; + napi_value result = nullptr; + + GET_PARAMS(env, info, ARGS_TWO); + NAPI_ASSERT(env, argc >= ARGS_ONE, "requires 1 parameter minimum"); + + unique_ptr asyncContext = make_unique(); + + status = napi_unwrap(env, thisVar, reinterpret_cast(&asyncContext->objectInfo)); + if (status == napi_ok && asyncContext->objectInfo != nullptr) { + for (size_t i = PARAM0; i < argc; i++) { + napi_valuetype valueType = napi_undefined; + napi_typeof(env, argv[i], &valueType); + + if (i == PARAM0 && valueType == napi_number) { + napi_get_value_int32(env, argv[i], &asyncContext->ringMode); + } else if (i == PARAM1 && valueType == napi_function) { + napi_create_reference(env, argv[i], refCount, &asyncContext->callbackRef); + break; + } else { + NAPI_ASSERT(env, false, "type mismatch"); + } + } + + if (asyncContext->callbackRef == nullptr) { + napi_create_promise(env, &asyncContext->deferred, &result); + } else { + napi_get_undefined(env, &result); + } + + napi_value resource = nullptr; + napi_create_string_utf8(env, "SetRingerMode", NAPI_AUTO_LENGTH, &resource); + + status = napi_create_async_work( + env, nullptr, resource, + [](napi_env env, void* data) { + auto context = static_cast(data); + context->status = + context->objectInfo->audioMngr_->SetRingerMode(GetNativeAudioRingerMode(context->ringMode)); + }, + SetFunctionAsyncCallbackComplete, static_cast(asyncContext.get()), &asyncContext->work); + if (status != napi_ok) { + result = nullptr; + } else { + status = napi_queue_async_work(env, asyncContext->work); + if (status == napi_ok) { + asyncContext.release(); + } else { + result = nullptr; + } + } + } + + return result; +} + +napi_value AudioManagerNapi::GetRingerMode(napi_env env, napi_callback_info info) +{ + napi_status status; + const int32_t refCount = 1; + napi_value result = nullptr; + + GET_PARAMS(env, info, ARGS_ONE); + + unique_ptr asyncContext = make_unique(); + + status = napi_unwrap(env, thisVar, reinterpret_cast(&asyncContext->objectInfo)); + if (status == napi_ok && asyncContext->objectInfo != nullptr) { + for (size_t i = PARAM0; i < argc; i++) { + napi_valuetype valueType = napi_undefined; + napi_typeof(env, argv[i], &valueType); + + if (i == PARAM0 && valueType == napi_function) { + napi_create_reference(env, argv[i], refCount, &asyncContext->callbackRef); + break; + } else { + NAPI_ASSERT(env, false, "type mismatch"); + } + } + + if (asyncContext->callbackRef == nullptr) { + napi_create_promise(env, &asyncContext->deferred, &result); + } else { + napi_get_undefined(env, &result); + } + + napi_value resource = nullptr; + napi_create_string_utf8(env, "GetRingerMode", NAPI_AUTO_LENGTH, &resource); + + status = napi_create_async_work( + env, nullptr, resource, + [](napi_env env, void* data) { + auto context = static_cast(data); + context->ringMode = GetJsAudioRingMode(context->objectInfo->audioMngr_->GetRingerMode()); + context->intValue = context->ringMode; + context->status = 0; + }, + GetIntValueAsyncCallbackComplete, static_cast(asyncContext.get()), &asyncContext->work); + if (status != napi_ok) { + result = nullptr; + } else { + status = napi_queue_async_work(env, asyncContext->work); + if (status == napi_ok) { + asyncContext.release(); + } else { + result = nullptr; + } + } + } + + return result; +} + +napi_value AudioManagerNapi::SetStreamMute(napi_env env, napi_callback_info info) +{ + napi_status status; + const int32_t refCount = 1; + napi_value result = nullptr; + + GET_PARAMS(env, info, ARGS_THREE); + NAPI_ASSERT(env, argc >= ARGS_TWO, "requires 2 parameters minimum"); + + unique_ptr asyncContext = make_unique(); + + status = napi_unwrap(env, thisVar, reinterpret_cast(&asyncContext->objectInfo)); + if (status == napi_ok && asyncContext->objectInfo != nullptr) { + for (size_t i = PARAM0; i < argc; i++) { + napi_valuetype valueType = napi_undefined; + napi_typeof(env, argv[i], &valueType); + + if (i == PARAM0 && valueType == napi_number) { + napi_get_value_int32(env, argv[i], &asyncContext->volType); + } else if (i == PARAM1 && valueType == napi_boolean) { + napi_get_value_bool(env, argv[i], &asyncContext->isMute); + } else if (i == PARAM2 && valueType == napi_function) { + napi_create_reference(env, argv[i], refCount, &asyncContext->callbackRef); + break; + } else { + NAPI_ASSERT(env, false, "type mismatch"); + } + } + + if (asyncContext->callbackRef == nullptr) { + napi_create_promise(env, &asyncContext->deferred, &result); + } else { + napi_get_undefined(env, &result); + } + + napi_value resource = nullptr; + napi_create_string_utf8(env, "SetStreamMute", NAPI_AUTO_LENGTH, &resource); + + status = napi_create_async_work( + env, nullptr, resource, + [](napi_env env, void* data) { + auto context = static_cast(data); + context->status = context->objectInfo->audioMngr_->SetMute(GetNativeAudioVolumeType(context->volType), + context->isMute); + }, + SetFunctionAsyncCallbackComplete, static_cast(asyncContext.get()), &asyncContext->work); + if (status != napi_ok) { + result = nullptr; + } else { + status = napi_queue_async_work(env, asyncContext->work); + if (status == napi_ok) { + asyncContext.release(); + } else { + result = nullptr; + } + } + } + + return result; +} + +napi_value AudioManagerNapi::IsStreamMute(napi_env env, napi_callback_info info) +{ + napi_status status; + const int32_t refCount = 1; + napi_value result = nullptr; + + GET_PARAMS(env, info, ARGS_TWO); + NAPI_ASSERT(env, argc >= ARGS_ONE, "requires 1 parameter minimum"); + + unique_ptr asyncContext = make_unique(); + + status = napi_unwrap(env, thisVar, reinterpret_cast(&asyncContext->objectInfo)); + if (status == napi_ok && asyncContext->objectInfo != nullptr) { + for (size_t i = PARAM0; i < argc; i++) { + napi_valuetype valueType = napi_undefined; + napi_typeof(env, argv[i], &valueType); + + if (i == PARAM0 && valueType == napi_number) { + napi_get_value_int32(env, argv[i], &asyncContext->volType); + } else if (i == PARAM1 && valueType == napi_function) { + napi_create_reference(env, argv[i], refCount, &asyncContext->callbackRef); + break; + } else { + NAPI_ASSERT(env, false, "type mismatch"); + } + } + + if (asyncContext->callbackRef == nullptr) { + napi_create_promise(env, &asyncContext->deferred, &result); + } else { + napi_get_undefined(env, &result); + } + + napi_value resource = nullptr; + napi_create_string_utf8(env, "IsStreamMute", NAPI_AUTO_LENGTH, &resource); + + status = napi_create_async_work( + env, nullptr, resource, + [](napi_env env, void* data) { + auto context = static_cast(data); + context->isMute = + context->objectInfo->audioMngr_->IsStreamMute(GetNativeAudioVolumeType(context->volType)); + context->isTrue = context->isMute; + context->status = 0; + }, + IsTrueAsyncCallbackComplete, static_cast(asyncContext.get()), &asyncContext->work); + if (status != napi_ok) { + result = nullptr; + } else { + status = napi_queue_async_work(env, asyncContext->work); + if (status == napi_ok) { + asyncContext.release(); + } else { + result = nullptr; + } + } + } + + return result; +} + +napi_value AudioManagerNapi::IsStreamActive(napi_env env, napi_callback_info info) +{ + napi_status status; + const int32_t refCount = 1; + napi_value result = nullptr; + + GET_PARAMS(env, info, ARGS_TWO); + NAPI_ASSERT(env, argc >= ARGS_ONE, "requires 1 parameter minimum"); + + unique_ptr asyncContext = make_unique(); + + status = napi_unwrap(env, thisVar, reinterpret_cast(&asyncContext->objectInfo)); + if (status == napi_ok && asyncContext->objectInfo != nullptr) { + for (size_t i = PARAM0; i < argc; i++) { + napi_valuetype valueType = napi_undefined; + napi_typeof(env, argv[i], &valueType); + + if (i == PARAM0 && valueType == napi_number) { + napi_get_value_int32(env, argv[i], &asyncContext->volType); + } else if (i == PARAM1 && valueType == napi_function) { + napi_create_reference(env, argv[i], refCount, &asyncContext->callbackRef); + break; + } else { + NAPI_ASSERT(env, false, "type mismatch"); + } + } + + if (asyncContext->callbackRef == nullptr) { + napi_create_promise(env, &asyncContext->deferred, &result); + } else { + napi_get_undefined(env, &result); + } + + napi_value resource = nullptr; + napi_create_string_utf8(env, "IsStreamActive", NAPI_AUTO_LENGTH, &resource); + + status = napi_create_async_work( + env, nullptr, resource, + [](napi_env env, void* data) { + auto context = static_cast(data); + context->isActive = + context->objectInfo->audioMngr_->IsStreamActive(GetNativeAudioVolumeType(context->volType)); + context->isTrue = context->isActive; + context->status = 0; + }, + IsTrueAsyncCallbackComplete, static_cast(asyncContext.get()), &asyncContext->work); + if (status != napi_ok) { + result = nullptr; + } else { + status = napi_queue_async_work(env, asyncContext->work); + if (status == napi_ok) { + asyncContext.release(); + } else { + result = nullptr; + } + } + } + + return result; +} + +napi_value AudioManagerNapi::SetDeviceActive(napi_env env, napi_callback_info info) +{ + napi_status status; + const int32_t refCount = 1; + napi_value result = nullptr; + + GET_PARAMS(env, info, ARGS_THREE); + NAPI_ASSERT(env, argc >= ARGS_TWO, "requires 2 parameters minimum"); + + unique_ptr asyncContext = make_unique(); + + status = napi_unwrap(env, thisVar, reinterpret_cast(&asyncContext->objectInfo)); + if (status == napi_ok && asyncContext->objectInfo != nullptr) { + for (size_t i = PARAM0; i < argc; i++) { + napi_valuetype valueType = napi_undefined; + napi_typeof(env, argv[i], &valueType); + + if (i == PARAM0 && valueType == napi_number) { + napi_get_value_int32(env, argv[i], &asyncContext->deviceType); + } else if (i == PARAM1 && valueType == napi_boolean) { + napi_get_value_bool(env, argv[i], &asyncContext->isActive); + } else if (i == PARAM2 && valueType == napi_function) { + napi_create_reference(env, argv[i], refCount, &asyncContext->callbackRef); + break; + } else { + NAPI_ASSERT(env, false, "type mismatch"); + } + } + + if (asyncContext->callbackRef == nullptr) { + napi_create_promise(env, &asyncContext->deferred, &result); + } else { + napi_get_undefined(env, &result); + } + + napi_value resource = nullptr; + napi_create_string_utf8(env, "SetDeviceActive", NAPI_AUTO_LENGTH, &resource); + + status = napi_create_async_work( + env, nullptr, resource, + [](napi_env env, void* data) { + auto context = static_cast(data); + context->status = context->objectInfo->audioMngr_->SetDeviceActive( + GetNativeDeviceType(context->deviceType), context->isActive); + }, + SetFunctionAsyncCallbackComplete, static_cast(asyncContext.get()), &asyncContext->work); + if (status != napi_ok) { + result = nullptr; + } else { + status = napi_queue_async_work(env, asyncContext->work); + if (status == napi_ok) { + asyncContext.release(); + } else { + result = nullptr; + } + } + } + + return result; +} + +napi_value AudioManagerNapi::IsDeviceActive(napi_env env, napi_callback_info info) +{ + napi_status status; + const int32_t refCount = 1; + napi_value result = nullptr; + + GET_PARAMS(env, info, ARGS_TWO); + NAPI_ASSERT(env, argc >= ARGS_ONE, "requires 1 parameter minimum"); + + unique_ptr asyncContext = make_unique(); + + status = napi_unwrap(env, thisVar, reinterpret_cast(&asyncContext->objectInfo)); + if (status == napi_ok && asyncContext->objectInfo != nullptr) { + for (size_t i = PARAM0; i < argc; i++) { + napi_valuetype valueType = napi_undefined; + napi_typeof(env, argv[i], &valueType); + + if (i == PARAM0 && valueType == napi_number) { + napi_get_value_int32(env, argv[i], &asyncContext->deviceType); + } else if (i == PARAM1 && valueType == napi_function) { + napi_create_reference(env, argv[i], refCount, &asyncContext->callbackRef); + break; + } else { + NAPI_ASSERT(env, false, "type mismatch"); + } + } + + if (asyncContext->callbackRef == nullptr) { + napi_create_promise(env, &asyncContext->deferred, &result); + } else { + napi_get_undefined(env, &result); + } + + napi_value resource = nullptr; + napi_create_string_utf8(env, "IsDeviceActive", NAPI_AUTO_LENGTH, &resource); + + status = napi_create_async_work( + env, nullptr, resource, + [](napi_env env, void* data) { + auto context = static_cast(data); + context->isActive = + context->objectInfo->audioMngr_->IsDeviceActive(GetNativeDeviceType(context->deviceType)); + context->isTrue = context->isActive; + context->status = 0; + }, + IsTrueAsyncCallbackComplete, static_cast(asyncContext.get()), &asyncContext->work); + if (status != napi_ok) { + result = nullptr; + } else { + status = napi_queue_async_work(env, asyncContext->work); + if (status == napi_ok) { + asyncContext.release(); + } else { + result = nullptr; + } + } + } + + return result; +} + +napi_value AudioManagerNapi::SetAudioParameter(napi_env env, napi_callback_info info) +{ + napi_status status; + const int32_t refCount = 1; + napi_value result = nullptr; + + GET_PARAMS(env, info, ARGS_THREE); + NAPI_ASSERT(env, argc >= ARGS_TWO, "requires 2 parameters minimum"); + + unique_ptr asyncContext = make_unique(); + + status = napi_unwrap(env, thisVar, reinterpret_cast(&asyncContext->objectInfo)); + if (status == napi_ok && asyncContext->objectInfo != nullptr) { + for (size_t i = PARAM0; i < argc; i++) { + napi_valuetype valueType = napi_undefined; + napi_typeof(env, argv[i], &valueType); + + if (i == PARAM0 && valueType == napi_string) { + asyncContext->key = GetStringArgument(env, argv[i]); + } else if (i == PARAM1 && valueType == napi_string) { + asyncContext->valueStr = GetStringArgument(env, argv[i]); + } else if (i == PARAM2 && valueType == napi_function) { + napi_create_reference(env, argv[i], refCount, &asyncContext->callbackRef); + break; + } else { + NAPI_ASSERT(env, false, "type mismatch"); + } + } + + if (asyncContext->callbackRef == nullptr) { + napi_create_promise(env, &asyncContext->deferred, &result); + } else { + napi_get_undefined(env, &result); + } + + napi_value resource = nullptr; + napi_create_string_utf8(env, "SetAudioParameter", NAPI_AUTO_LENGTH, &resource); status = napi_create_async_work( env, nullptr, resource, [](napi_env env, void* data) { - AudioManagerAsyncContext* context = (AudioManagerAsyncContext*) data; - context->volLevel = - context->objectInfo->audioMngr_->GetMaxVolume(GetNativeAudioVolumeType(context->volType)); + auto context = static_cast(data); + context->objectInfo->audioMngr_->SetAudioParameter(context->key, context->valueStr); context->status = 0; }, - GetMaxVolumeAsyncCallbackComplete, (void*)asyncContext, &asyncContext->work); + SetFunctionAsyncCallbackComplete, static_cast(asyncContext.get()), &asyncContext->work); if (status != napi_ok) { - delete asyncContext; result = nullptr; } else { - napi_queue_async_work(env, asyncContext->work); + status = napi_queue_async_work(env, asyncContext->work); + if (status == napi_ok) { + asyncContext.release(); + } else { + result = nullptr; + } } - } else { - delete asyncContext; } + return result; } -static void GetMinVolumeAsyncCallbackComplete(napi_env env, napi_status status, void* data) +napi_value AudioManagerNapi::GetAudioParameter(napi_env env, napi_callback_info info) { - AudioManagerAsyncContext* asyncContext = (AudioManagerAsyncContext*) data; - napi_value result[ARGS_TWO] = {0}; - napi_value retVal; + napi_status status; + const int32_t refCount = 1; + napi_value result = nullptr; - napi_get_undefined(env, &result[PARAM0]); - napi_create_int32(env, asyncContext->volLevel, &result[PARAM1]); + GET_PARAMS(env, info, ARGS_TWO); + NAPI_ASSERT(env, argc >= ARGS_ONE, "requires 1 parameter minimum"); - if (asyncContext->deferred) { - napi_resolve_deferred(env, asyncContext->deferred, result[PARAM1]); - } else { - napi_value callback = nullptr; - napi_get_reference_value(env, asyncContext->callbackRef, &callback); - napi_call_function(env, nullptr, callback, ARGS_TWO, result, &retVal); - napi_delete_reference(env, asyncContext->callbackRef); + unique_ptr asyncContext = make_unique(); + + status = napi_unwrap(env, thisVar, reinterpret_cast(&asyncContext->objectInfo)); + if (status == napi_ok && asyncContext->objectInfo != nullptr) { + for (size_t i = PARAM0; i < argc; i++) { + napi_valuetype valueType = napi_undefined; + napi_typeof(env, argv[i], &valueType); + + if (i == PARAM0 && valueType == napi_string) { + asyncContext->key = GetStringArgument(env, argv[i]); + } else if (i == PARAM1 && valueType == napi_function) { + napi_create_reference(env, argv[i], refCount, &asyncContext->callbackRef); + break; + } else { + NAPI_ASSERT(env, false, "type mismatch"); + } + } + + if (asyncContext->callbackRef == nullptr) { + napi_create_promise(env, &asyncContext->deferred, &result); + } else { + napi_get_undefined(env, &result); + } + + napi_value resource = nullptr; + napi_create_string_utf8(env, "GetAudioParameter", NAPI_AUTO_LENGTH, &resource); + status = napi_create_async_work( + env, nullptr, resource, + [](napi_env env, void* data) { + auto context = static_cast(data); + context->valueStr = context->objectInfo->audioMngr_->GetAudioParameter(context->key); + context->status = 0; + }, + GetStringValueAsyncCallbackComplete, static_cast(asyncContext.get()), &asyncContext->work); + if (status != napi_ok) { + result = nullptr; + } else { + status = napi_queue_async_work(env, asyncContext->work); + if (status == napi_ok) { + asyncContext.release(); + } else { + result = nullptr; + } + } } - napi_delete_async_work(env, asyncContext->work); - delete asyncContext; + + return result; +} + +napi_value AudioManagerNapi::SetVolume(napi_env env, napi_callback_info info) +{ + napi_status status; + const int32_t refCount = 1; + napi_value result = nullptr; + + GET_PARAMS(env, info, ARGS_THREE); + NAPI_ASSERT(env, argc >= ARGS_TWO, "requires 2 parameters minimum"); + + unique_ptr asyncContext = make_unique(); + + status = napi_unwrap(env, thisVar, reinterpret_cast(&asyncContext->objectInfo)); + if (status == napi_ok && asyncContext->objectInfo != nullptr) { + for (size_t i = PARAM0; i < argc; i++) { + napi_valuetype valueType = napi_undefined; + napi_typeof(env, argv[i], &valueType); + + if (i == PARAM0 && valueType == napi_number) { + napi_get_value_int32(env, argv[i], &asyncContext->volType); + } else if (i == PARAM1 && valueType == napi_number) { + napi_get_value_double(env, argv[i], &asyncContext->volLevel); + } else if (i == PARAM2 && valueType == napi_function) { + napi_create_reference(env, argv[i], refCount, &asyncContext->callbackRef); + break; + } else { + NAPI_ASSERT(env, false, "type mismatch"); + } + } + + if (asyncContext->callbackRef == nullptr) { + napi_create_promise(env, &asyncContext->deferred, &result); + } else { + napi_get_undefined(env, &result); + } + + napi_value resource = nullptr; + napi_create_string_utf8(env, "SetVolume", NAPI_AUTO_LENGTH, &resource); + + status = napi_create_async_work( + env, nullptr, resource, + [](napi_env env, void* data) { + auto context = static_cast(data); + context->status = context->objectInfo->audioMngr_->SetVolume(GetNativeAudioVolumeType(context->volType), + static_cast(context->volLevel)); + }, + SetFunctionAsyncCallbackComplete, static_cast(asyncContext.get()), &asyncContext->work); + if (status != napi_ok) { + result = nullptr; + } else { + status = napi_queue_async_work(env, asyncContext->work); + if (status == napi_ok) { + asyncContext.release(); + } else { + result = nullptr; + } + } + } + + return result; +} + +napi_value AudioManagerNapi::GetVolume(napi_env env, napi_callback_info info) +{ + napi_status status; + const int32_t refCount = 1; + napi_value result = nullptr; + + GET_PARAMS(env, info, ARGS_TWO); + NAPI_ASSERT(env, argc >= ARGS_ONE, "requires 1 parameter minimum"); + + unique_ptr asyncContext = make_unique(); + + status = napi_unwrap(env, thisVar, reinterpret_cast(&asyncContext->objectInfo)); + if (status == napi_ok && asyncContext->objectInfo != nullptr) { + for (size_t i = PARAM0; i < argc; i++) { + napi_valuetype valueType = napi_undefined; + napi_typeof(env, argv[i], &valueType); + + if (i == PARAM0 && valueType == napi_number) { + napi_get_value_int32(env, argv[i], &asyncContext->volType); + } else if (i == PARAM1 && valueType == napi_function) { + napi_create_reference(env, argv[i], refCount, &asyncContext->callbackRef); + break; + } else { + NAPI_ASSERT(env, false, "type mismatch"); + } + } + + if (asyncContext->callbackRef == nullptr) { + napi_create_promise(env, &asyncContext->deferred, &result); + } else { + napi_get_undefined(env, &result); + } + + napi_value resource = nullptr; + napi_create_string_utf8(env, "GetVolume", NAPI_AUTO_LENGTH, &resource); + + status = napi_create_async_work( + env, nullptr, resource, + [](napi_env env, void* data) { + auto context = static_cast(data); + context->volLevel = static_cast(context->objectInfo->audioMngr_->GetVolume( + GetNativeAudioVolumeType(context->volType))); + context->doubleValue = context->volLevel; + context->status = 0; + }, + GetDoubleValueAsyncCallbackComplete, static_cast(asyncContext.get()), &asyncContext->work); + if (status != napi_ok) { + result = nullptr; + } else { + status = napi_queue_async_work(env, asyncContext->work); + if (status == napi_ok) { + asyncContext.release(); + } else { + result = nullptr; + } + } + } + + return result; +} + +napi_value AudioManagerNapi::GetMaxVolume(napi_env env, napi_callback_info info) +{ + napi_status status; + const int32_t refCount = 1; + napi_value result = nullptr; + + GET_PARAMS(env, info, ARGS_TWO); + NAPI_ASSERT(env, argc >= ARGS_ONE, "requires 1 parameter minimum"); + + unique_ptr asyncContext = make_unique(); + + status = napi_unwrap(env, thisVar, reinterpret_cast(&asyncContext->objectInfo)); + if (status == napi_ok && asyncContext->objectInfo != nullptr) { + for (size_t i = PARAM0; i < argc; i++) { + napi_valuetype valueType = napi_undefined; + napi_typeof(env, argv[i], &valueType); + + if (i == PARAM0 && valueType == napi_number) { + napi_get_value_int32(env, argv[i], &asyncContext->volType); + } else if (i == PARAM1 && valueType == napi_function) { + napi_create_reference(env, argv[i], refCount, &asyncContext->callbackRef); + break; + } else { + NAPI_ASSERT(env, false, "type mismatch"); + } + } + + if (asyncContext->callbackRef == nullptr) { + napi_create_promise(env, &asyncContext->deferred, &result); + } else { + napi_get_undefined(env, &result); + } + + napi_value resource = nullptr; + napi_create_string_utf8(env, "GetMaxVolume", NAPI_AUTO_LENGTH, &resource); + + status = napi_create_async_work( + env, nullptr, resource, + [](napi_env env, void* data) { + auto context = static_cast(data); + context->volLevel = static_cast(context->objectInfo->audioMngr_->GetMaxVolume( + GetNativeAudioVolumeType(context->volType))); + context->doubleValue = context->volLevel; + context->status = 0; + }, + GetDoubleValueAsyncCallbackComplete, static_cast(asyncContext.get()), &asyncContext->work); + if (status != napi_ok) { + result = nullptr; + } else { + status = napi_queue_async_work(env, asyncContext->work); + if (status == napi_ok) { + asyncContext.release(); + } else { + result = nullptr; + } + } + } + + return result; } napi_value AudioManagerNapi::GetMinVolume(napi_env env, napi_callback_info info) @@ -641,10 +1536,10 @@ napi_value AudioManagerNapi::GetMinVolume(napi_env env, napi_callback_info info) napi_value result = nullptr; GET_PARAMS(env, info, ARGS_TWO); - NAPI_ASSERT(env, argc >= ARGS_ONE, "requires 1 parameters minimum"); + NAPI_ASSERT(env, argc >= ARGS_ONE, "requires 1 parameter minimum"); + + unique_ptr asyncContext = make_unique(); - HiLog::Debug(LABEL, "AudioManagerNapi::GetMinVolume() is called!"); - AudioManagerAsyncContext* asyncContext = new AudioManagerAsyncContext(); status = napi_unwrap(env, thisVar, reinterpret_cast(&asyncContext->objectInfo)); if (status == napi_ok && asyncContext->objectInfo != nullptr) { for (size_t i = PARAM0; i < argc; i++) { @@ -657,7 +1552,6 @@ napi_value AudioManagerNapi::GetMinVolume(napi_env env, napi_callback_info info) napi_create_reference(env, argv[i], refCount, &asyncContext->callbackRef); break; } else { - delete asyncContext; NAPI_ASSERT(env, false, "type mismatch"); } } @@ -674,39 +1568,40 @@ napi_value AudioManagerNapi::GetMinVolume(napi_env env, napi_callback_info info) status = napi_create_async_work( env, nullptr, resource, [](napi_env env, void* data) { - AudioManagerAsyncContext* context = (AudioManagerAsyncContext*) data; - context->volLevel = - context->objectInfo->audioMngr_->GetMinVolume(GetNativeAudioVolumeType(context->volType)); + auto context = static_cast(data); + context->volLevel = static_cast(context->objectInfo->audioMngr_->GetMinVolume( + GetNativeAudioVolumeType(context->volType))); + context->doubleValue = context->volLevel; context->status = 0; }, - GetMinVolumeAsyncCallbackComplete, (void*)asyncContext, &asyncContext->work); + GetDoubleValueAsyncCallbackComplete, static_cast(asyncContext.get()), &asyncContext->work); if (status != napi_ok) { - delete asyncContext; result = nullptr; } else { - napi_queue_async_work(env, asyncContext->work); + status = napi_queue_async_work(env, asyncContext->work); + if (status == napi_ok) { + asyncContext.release(); + } else { + result = nullptr; + } } - } else { - delete asyncContext; } + return result; } static void GetDevicesAsyncCallbackComplete(napi_env env, napi_status status, void* data) { - AudioManagerAsyncContext* asyncContext = (AudioManagerAsyncContext*) data; + auto asyncContext = static_cast(data); napi_value result[ARGS_TWO] = {0}; napi_value ddWrapper = nullptr; napi_value retVal; int32_t size = asyncContext->deviceDescriptors.size(); - HiLog::Debug(LABEL, "Size of Device Descriptors array, %{public}d!, deviceflag: %{public}d", - size, asyncContext->deviceFlag); + napi_create_array_with_length(env, size, &result[PARAM1]); for (int i = 0; i < size; i += 1) { AudioDeviceDescriptor *curDeviceDescriptor = asyncContext->deviceDescriptors[i]; - HiLog::Debug(LABEL, "Device role, %{public}d, Device type: %{public}d", - curDeviceDescriptor->deviceRole_, curDeviceDescriptor->deviceType_); ddWrapper = AudioDeviceDescriptorNapi::CreateAudioDeviceDescriptorWrapper(env, curDeviceDescriptor); napi_set_element(env, result[PARAM1], i, ddWrapper); } @@ -722,6 +1617,7 @@ static void GetDevicesAsyncCallbackComplete(napi_env env, napi_status status, vo napi_delete_reference(env, asyncContext->callbackRef); } napi_delete_async_work(env, asyncContext->work); + delete asyncContext; } @@ -732,10 +1628,10 @@ napi_value AudioManagerNapi::GetDevices(napi_env env, napi_callback_info info) napi_value result = nullptr; GET_PARAMS(env, info, ARGS_TWO); - NAPI_ASSERT(env, argc >= ARGS_ONE, "requires 1 parameters minimum"); + NAPI_ASSERT(env, argc >= ARGS_ONE, "requires 1 parameter minimum"); + + unique_ptr asyncContext = make_unique(); - HiLog::Debug(LABEL, "AudioManagerNapi::GetDevices() is called!"); - AudioManagerAsyncContext* asyncContext = new AudioManagerAsyncContext(); status = napi_unwrap(env, thisVar, reinterpret_cast(&asyncContext->objectInfo)); if (status == napi_ok && asyncContext->objectInfo != nullptr) { for (size_t i = PARAM0; i < argc; i++) { @@ -748,7 +1644,6 @@ napi_value AudioManagerNapi::GetDevices(napi_env env, napi_callback_info info) napi_create_reference(env, argv[i], refCount, &asyncContext->callbackRef); break; } else { - delete asyncContext; NAPI_ASSERT(env, false, "type mismatch"); } } @@ -765,21 +1660,24 @@ napi_value AudioManagerNapi::GetDevices(napi_env env, napi_callback_info info) status = napi_create_async_work( env, nullptr, resource, [](napi_env env, void* data) { - AudioManagerAsyncContext* context = (AudioManagerAsyncContext*) data; + auto context = static_cast(data); context->deviceDescriptors = context->objectInfo->audioMngr_->GetDevices(GetNativeDeviceFlag(context->deviceFlag)); context->status = 0; }, - GetDevicesAsyncCallbackComplete, (void*)asyncContext, &asyncContext->work); + GetDevicesAsyncCallbackComplete, static_cast(asyncContext.get()), &asyncContext->work); if (status != napi_ok) { - delete asyncContext; result = nullptr; } else { - napi_queue_async_work(env, asyncContext->work); + status = napi_queue_async_work(env, asyncContext->work); + if (status == napi_ok) { + asyncContext.release(); + } else { + result = nullptr; + } } - } else { - delete asyncContext; } + return result; } @@ -802,6 +1700,7 @@ static napi_module g_module = { extern "C" __attribute__((constructor)) void RegisterModule(void) { - HiLog::Debug(LABEL, "RegisterModule() is called!"); napi_module_register(&g_module); } +} // namespace AudioStandard +} // namespace OHOS diff --git a/interfaces/innerkits/native/audiocommon/include/audio_info.h b/interfaces/innerkits/native/audiocommon/include/audio_info.h new file mode 100644 index 0000000000000000000000000000000000000000..21c16df9d7a9e7fd468cfd3beb4e57af2b784251 --- /dev/null +++ b/interfaces/innerkits/native/audiocommon/include/audio_info.h @@ -0,0 +1,244 @@ +/* + * Copyright (C) 2021 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_INFO_H +#define AUDIO_INFO_H + +#ifdef __MUSL__ +#include +#endif // __MUSL__ + +#include +#include + +namespace OHOS { +namespace AudioStandard { +// Audio Device Types +enum DeviceType { + /** + * Indicates device type none. + */ + DEVICE_TYPE_NONE = -1, + /** + * Indicates a speaker built in a device. + */ + SPEAKER = 0, + /** + * Indicates a headset, which is the combination of a pair of headphones and a microphone. + */ + WIRED_HEADSET = 1, + /** + * Indicates a Bluetooth device used for telephony. + */ + BLUETOOTH_SCO = 2, + /** + * Indicates a Bluetooth device supporting the Advanced Audio Distribution Profile (A2DP). + */ + BLUETOOTH_A2DP = 3, + /** + * Indicates a microphone built in a device. + */ + MIC = 4 +}; + +// Audio Role +enum DeviceRole { + /** + * Device role none. + */ + DEVICE_ROLE_NONE = -1, + /** + * Input device role. + */ + INPUT_DEVICE = 0, + /** + * Output device role. + */ + OUTPUT_DEVICE = 1 +}; + +enum AudioStreamType { + /** + * Indicates audio streams default. + */ + STREAM_DEFAULT = -1, + /** + * Indicates audio streams media. + */ + STREAM_MEDIA = 0, + /** + * Indicates audio streams of voices in calls. + */ + STREAM_VOICE_CALL = 1, + /** + * Indicates audio streams for system sounds. + */ + STREAM_SYSTEM = 2, + /** + * Indicates audio streams for ringtones. + */ + STREAM_RING = 3, + /** + * Indicates audio streams for music playback. + */ + STREAM_MUSIC = 4, + /** + * Indicates audio streams for alarms. + */ + STREAM_ALARM = 5, + /** + * Indicates audio streams for notifications. + */ + STREAM_NOTIFICATION = 6, + /** + * Indicates audio streams for voice calls routed through a connected Bluetooth device. + */ + STREAM_BLUETOOTH_SCO = 7, + /** + * Indicates audio streams for enforced audible. + */ + STREAM_ENFORCED_AUDIBLE = 8, + /** + * Indicates audio streams for dual-tone multi-frequency (DTMF) tones. + */ + STREAM_DTMF = 9, + /** + * Indicates audio streams exclusively transmitted through the speaker (text-to-speech) of a device. + */ + STREAM_TTS = 10, + /** + * Indicates audio streams used for prompts in terms of accessibility. + */ + STREAM_ACCESSIBILITY = 11 +}; + +enum AudioEncodingType { + ENCODING_PCM = 0, + ENCODING_AAC, // Currently not supported + ENCODING_INVALID +}; + +// Ringer Mode +enum AudioRingerMode { + RINGER_MODE_NORMAL = 0, + RINGER_MODE_SILENT = 1, + RINGER_MODE_VIBRATE = 2 +}; + +// format +enum AudioSampleFormat { + SAMPLE_U8 = 8, + SAMPLE_S16LE = 16, + SAMPLE_S24LE = 24, + SAMPLE_S32LE = 32, + INVALID_WIDTH = -1 +}; + +// channel +enum AudioChannel { + MONO = 1, + STEREO +}; + +// sampling rate +enum AudioSamplingRate { + SAMPLE_RATE_8000 = 8000, + SAMPLE_RATE_11025 = 11025, + SAMPLE_RATE_12000 = 12000, + SAMPLE_RATE_16000 = 16000, + SAMPLE_RATE_22050 = 22050, + SAMPLE_RATE_24000 = 24000, + SAMPLE_RATE_32000 = 32000, + SAMPLE_RATE_44100 = 44100, + SAMPLE_RATE_48000 = 48000, + SAMPLE_RATE_64000 = 64000, + SAMPLE_RATE_96000 = 96000 +}; + +typedef enum { + /** Invalid audio source */ + AUDIO_SOURCE_INVALID = -1, + /** Default audio source */ + AUDIO_SOURCE_DEFAULT = 0, + /** Microphone */ + AUDIO_MIC = 1, + /** Uplink voice */ + AUDIO_VOICE_UPLINK = 2, + /** Downlink voice */ + AUDIO_VOICE_DOWNLINK = 3, + /** Voice call */ + AUDIO_VOICE_CALL = 4, + /** Camcorder */ + AUDIO_CAMCORDER = 5, + /** Voice recognition */ + AUDIO_VOICE_RECOGNITION = 6, + /** Voice communication */ + AUDIO_VOICE_COMMUNICATION = 7, + /** Remote submix */ + AUDIO_REMOTE_SUBMIX = 8, + /** Unprocessed audio */ + AUDIO_UNPROCESSED = 9, + /** Voice performance */ + AUDIO_VOICE_PERFORMANCE = 10, + /** Echo reference */ + AUDIO_ECHO_REFERENCE = 1997, + /** Radio tuner */ + AUDIO_RADIO_TUNER = 1998, + /** Hotword */ + AUDIO_HOTWORD = 1999, + /** Extended remote submix */ + AUDIO_REMOTE_SUBMIX_EXTEND = 10007, +} AudioSourceType; + +struct AudioStreamParams { + uint32_t samplingRate; + uint8_t encoding; + uint8_t format; + uint8_t channels; +}; + +// Supported audio parameters for both renderer and recorder +const std::vector AUDIO_SUPPORTED_FORMATS { + SAMPLE_U8, + SAMPLE_S16LE, + SAMPLE_S24LE, + SAMPLE_S32LE +}; + +const std::vector AUDIO_SUPPORTED_CHANNELS { + MONO, + STEREO +}; + +const std::vector AUDIO_SUPPORTED_ENCODING_TYPES { + ENCODING_PCM +}; + +const std::vector AUDIO_SUPPORTED_SAMPLING_RATES { + SAMPLE_RATE_8000, + SAMPLE_RATE_11025, + SAMPLE_RATE_12000, + SAMPLE_RATE_16000, + SAMPLE_RATE_22050, + SAMPLE_RATE_24000, + SAMPLE_RATE_32000, + SAMPLE_RATE_44100, + SAMPLE_RATE_48000, + SAMPLE_RATE_64000, + SAMPLE_RATE_96000 +}; +typedef void* AudioIOHandle; +} // namespace AudioStandard +} // namespace OHOS +#endif // AUDIO_INFO_H diff --git a/services/src/audio_device_descriptor.cpp b/interfaces/innerkits/native/audiocommon/include/timestamp.h old mode 100755 new mode 100644 similarity index 33% rename from services/src/audio_device_descriptor.cpp rename to interfaces/innerkits/native/audiocommon/include/timestamp.h index c89d98ca62d2379b61023c3f793a6c4a8648cefb..2295c3b85cc972cc61f97f9a2adf23a5e89526a2 --- a/services/src/audio_device_descriptor.cpp +++ b/interfaces/innerkits/native/audiocommon/include/timestamp.h @@ -1,53 +1,51 @@ -/* - * Copyright (C) 2021 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_device_descriptor.h" -#include "media_log.h" - -namespace OHOS { -/** - * @brief The AudioDeviceDescriptor class provides - * different sets of audio devices and their roles - */ -AudioDeviceDescriptor::AudioDeviceDescriptor() -{ - MEDIA_DEBUG_LOG("AudioDeviceDescriptor constructor"); - deviceType_ = DEVICE_TYPE_NONE; - deviceRole_ = DEVICE_ROLE_NONE; -} - -AudioDeviceDescriptor::~AudioDeviceDescriptor() -{ - MEDIA_DEBUG_LOG("AudioDeviceDescriptor::~AudioDeviceDescriptor"); -} - -bool AudioDeviceDescriptor::Marshalling(Parcel &parcel) const -{ - MEDIA_DEBUG_LOG("AudioDeviceDescriptor::Marshalling called"); - return parcel.WriteInt32(deviceType_) && parcel.WriteInt32(deviceRole_); -} - -AudioDeviceDescriptor *AudioDeviceDescriptor::Unmarshalling(Parcel &in) -{ - MEDIA_DEBUG_LOG("AudioDeviceDescriptor::Unmarshalling called"); - AudioDeviceDescriptor *audioDeviceDescriptor = new(std::nothrow) AudioDeviceDescriptor(); - if (audioDeviceDescriptor == nullptr) { - return nullptr; - } - audioDeviceDescriptor->deviceType_ = static_cast(in.ReadInt32()); - audioDeviceDescriptor->deviceRole_ = static_cast(in.ReadInt32()); - return audioDeviceDescriptor; -} -} // namespace OHOS +/* + * Copyright (C) 2021 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 TIMESTAMP_H +#define TIMESTAMP_H +#ifdef __MUSL__ +#include + +#include +#endif // __MUSL__ +#include + +namespace OHOS { +namespace AudioStandard { +/** + * @brief Represents Timestamp information, including the frame position information and high-resolution time source. + */ +class Timestamp { +public: + Timestamp() : framePosition(0) + { + time.tv_sec = 0; + time.tv_nsec = 0; + } + virtual ~Timestamp() = default; + uint32_t framePosition; + struct timespec time; + + /** + * @brief Enumerates the time base of this Timestamp. Different timing methods are supported. + * + */ + enum Timestampbase { + /** Monotonically increasing time, excluding the system sleep time */ + MONOTONIC = 0 + }; +}; +} // namespace AudioStandard +} // namespace OHOS +#endif // TIMESTAMP_H diff --git a/interfaces/innerkits/native/audiomanager/BUILD.gn b/interfaces/innerkits/native/audiomanager/BUILD.gn old mode 100755 new mode 100644 index 3389083eec649d62d4e89074f59b8566720982dd..7e065663fd94de2d5f4a9cae20653d85e4474dc9 --- a/interfaces/innerkits/native/audiomanager/BUILD.gn +++ b/interfaces/innerkits/native/audiomanager/BUILD.gn @@ -9,41 +9,110 @@ # 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. +# limitations under the License. import("//build/ohos.gni") +pulseaudio_dir = "//third_party/pulseaudio" +pulseaudio_build_path = "//foundation/multimedia/audio_standard/frameworks/innerkitsimpl/pulseaudio" + +group("audio_client_test_packages") { + deps = [ + ":playback_test", + ":record_test", + ] +} + config("audio_client_public_config") { include_dirs = [ "//foundation/multimedia/audio_standard/frameworks/innerkitsimpl/common/include", + "//foundation/multimedia/audio_standard/interfaces/innerkits/native/audiocommon/include", "//foundation/multimedia/audio_standard/interfaces/innerkits/native/audiomanager/include", + "//foundation/multimedia/audio_standard/interfaces/innerkits/native/audiopolicy/include", "//foundation/multimedia/audio_standard/services/include", "//foundation/multimedia/audio_standard/services/include/client", + "//foundation/multimedia/audio_standard/services/include/audiopolicy/client", "//utils/system/safwk/native/include", "//utils/native/base/include", + "//foundation/multimedia/audio_standard/interfaces/innerkits/native/audiosession/include", + "//foundation/multimedia/audio_standard/interfaces/innerkits/native/audiostream/include", + "$pulseaudio_dir/src", + "$pulseaudio_dir/confgure/src", + "$pulseaudio_build_path/include", + "//third_party/bounds_checking_function/include", + ] + + cflags = [ + "-Wall", + "-Werror", ] if (target_cpu == "arm") { - cflags = [ "-DBINDER_IPC_32BIT" ] + cflags += [ "-DBINDER_IPC_32BIT" ] } + } + ohos_shared_library("audio_client") { install_enable = true sources = [ - "//foundation/multimedia/audio_standard/services/src/audio_device_descriptor.cpp", "//foundation/multimedia/audio_standard/services/src/client/audio_manager_proxy.cpp", - "//foundation/multimedia/audio_standard/services/src/client/audio_svc_manager.cpp", + "//foundation/multimedia/audio_standard/services/src/client/audio_service_client.cpp", + "//foundation/multimedia/audio_standard/services/src/client/audio_session.cpp", + "//foundation/multimedia/audio_standard/services/src/client/audio_stream.cpp", + "//foundation/multimedia/audio_standard/services/src/client/audio_system_manager.cpp", ] - public_configs = [ ":audio_client_public_config" ] + public_configs = [ + ":audio_client_public_config", + ] - deps = [ "//utils/native/base:utils" ] + deps = [ + "//utils/native/base:utils", + "//foundation/multimedia/audio_standard/frameworks/innerkitsimpl/pulseaudio/src/pulse:pulse", + "//third_party/bounds_checking_function:libsec_static", + "//foundation/multimedia/audio_standard/interfaces/innerkits/native/audiopolicy:audio_policy_client", + ] external_deps = [ - "hiviewdfx_hilog_native:libhilog", "ipc:ipc_core", "samgr_L2:samgr_proxy", + "hiviewdfx_hilog_native:libhilog", ] subsystem_name = "multimedia" part_name = "multimedia_audio_standard" } + +ohos_executable("playback_test") { + install_enable = false + + sources = [ "//foundation/multimedia/audio_standard/services/test/playback_test.cpp" ] + + configs = [ + ":audio_client_public_config", + ] + + deps = [ + ":audio_client", + ] + + external_deps = [ "hiviewdfx_hilog_native:libhilog" ] + + part_name = "multimedia_audio_standard" + subsystem_name = "multimedia" +} + +ohos_executable("record_test") { + install_enable = false + + sources = [ "//foundation/multimedia/audio_standard/services/test/record_test.cpp" ] + + configs = [ ":audio_client_public_config" ] + + deps = [ ":audio_client" ] + + external_deps = [ "hiviewdfx_hilog_native:libhilog"] + + part_name = "multimedia_audio_standard" + subsystem_name = "multimedia" +} diff --git a/interfaces/innerkits/native/audiomanager/include/audio_svc_manager.h b/interfaces/innerkits/native/audiomanager/include/audio_svc_manager.h deleted file mode 100755 index 062c3628af235bc4a8a80a6bb4279df0ba73972d..0000000000000000000000000000000000000000 --- a/interfaces/innerkits/native/audiomanager/include/audio_svc_manager.h +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (C) 2021 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 ST_AUDIO_SVC_MANAGER_H -#define ST_AUDIO_SVC_MANAGER_H - -#include - -#include "audio_device_descriptor.h" - -namespace OHOS { -/** - * @brief The AudioSvcManager class is an abstract definition of audio manager. - * Provides a series of client/interfaces for audio management - */ - -class AudioSvcManager { -public: -enum AudioVolumeType { - /** - * Indicates audio streams of voices in calls. - */ - STREAM_VOICE_CALL = 1, - /** - * Indicates audio streams for system sounds. - */ - STREAM_SYSTEM = 2, - /** - * Indicates audio streams for ringtones. - */ - STREAM_RING = 3, - /** - * Indicates audio streams for music playback. - */ - STREAM_MUSIC = 4, - /** - * Indicates audio streams for alarms. - */ - STREAM_ALARM = 5, - /** - * Indicates audio streams for notifications. - */ - STREAM_NOTIFICATION = 6, - /** - * Indicates audio streams for voice calls routed through a connected Bluetooth device. - */ - STREAM_BLUETOOTH_SCO = 7, - /** - * Indicates audio streams for dual-tone multi-frequency (DTMF) tones. - */ - STREAM_DTMF = 8, - /** - * Indicates audio streams exclusively transmitted through the speaker (text-to-speech) of a device. - */ - STREAM_TTS = 9, - /** - * Indicates audio streams used for prompts in terms of accessibility. - */ - STREAM_ACCESSIBILITY = 10 - }; - static AudioSvcManager* GetInstance(); - void SetVolume(AudioSvcManager::AudioVolumeType volumeType, int32_t volume); - int GetVolume(AudioSvcManager::AudioVolumeType volumeType); - int GetMaxVolume(AudioSvcManager::AudioVolumeType volumeType); - int GetMinVolume(AudioSvcManager::AudioVolumeType volumeType); - std::vector> GetDevices(AudioDeviceDescriptor::DeviceFlag deviceFlag); -private: - AudioSvcManager(); - virtual ~AudioSvcManager(); - void init(); -}; -} // namespace OHOS -#endif // ST_AUDIO_SVC_MANAGER_H diff --git a/services/include/audio_device_descriptor.h b/interfaces/innerkits/native/audiomanager/include/audio_system_manager.h old mode 100755 new mode 100644 similarity index 43% rename from services/include/audio_device_descriptor.h rename to interfaces/innerkits/native/audiomanager/include/audio_system_manager.h index 7b62c3ec871632fa5f57b6ec2df3ca0044208f19..cde085460a19da073e9ea0912ba10d03c5be3b09 --- a/services/include/audio_device_descriptor.h +++ b/interfaces/innerkits/native/audiomanager/include/audio_system_manager.h @@ -1,95 +1,170 @@ -/* - * Copyright (C) 2021 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 ST_AUDIO_DEVICE_DESCRIPTOR_H -#define ST_AUDIO_DEVICE_DESCRIPTOR_H - -#include "parcel.h" - -namespace OHOS { -/** - * @brief The AudioDeviceDescriptor class provides different sets of audio devices and their roles - */ - -class AudioDeviceDescriptor : public Parcelable { -public: -enum DeviceFlag { - /** - * Indicates all output audio devices. - */ - OUTPUT_DEVICES_FLAG = 0, - /** - * Indicates all input audio devices. - */ - INPUT_DEVICES_FLAG = 1, - /** - * Indicates all audio devices. - */ - ALL_DEVICES_FLAG = 2 -}; - -enum DeviceRole { - /** - * Device role none. - */ - DEVICE_ROLE_NONE = -1, - /** - * Input device role. - */ - INPUT_DEVICE = 0, - /** - * Output device role. - */ - OUTPUT_DEVICE = 1 -}; - -enum DeviceType { - /** - * Indicates device type none. - */ - DEVICE_TYPE_NONE = -1, - /** - * Indicates a speaker built in a device. - */ - SPEAKER = 0, - /** - * Indicates a headset, which is the combination of a pair of headphones and a microphone. - */ - WIRED_HEADSET = 1, - /** - * Indicates a Bluetooth device used for telephony. - */ - BLUETOOTH_SCO = 2, - /** - * Indicates a Bluetooth device supporting the Advanced Audio Distribution Profile (A2DP). - */ - BLUETOOTH_A2DP = 3, - /** - * Indicates a microphone built in a device. - */ - MIC = 4 -}; - - DeviceType getType(); - DeviceRole getRole(); - DeviceType deviceType_; - DeviceRole deviceRole_; - AudioDeviceDescriptor(); - virtual ~AudioDeviceDescriptor(); - bool Marshalling(Parcel &parcel) const override; - static AudioDeviceDescriptor* Unmarshalling(Parcel &parcel); -}; -} // namespace OHOS -#endif // ST_AUDIO_DEVICE_DESCRIPTOR_H +/* + * Copyright (C) 2021 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 ST_AUDIO_SYSTEM_MANAGER_H +#define ST_AUDIO_SYSTEM_MANAGER_H + +#include + +#include "parcel.h" +#include "audio_info.h" + +namespace OHOS { +namespace AudioStandard { +class AudioDeviceDescriptor; +class AudioDeviceDescriptor : public Parcelable { + friend class AudioSystemManager; +public: +enum DeviceFlag { + /** + * Indicates all output audio devices. + */ + OUTPUT_DEVICES_FLAG = 0, + /** + * Indicates all input audio devices. + */ + INPUT_DEVICES_FLAG = 1, + /** + * Indicates all audio devices. + */ + ALL_DEVICES_FLAG = 2 +}; + +enum DeviceRole { + /** + * Device role none. + */ + DEVICE_ROLE_NONE = -1, + /** + * Input device role. + */ + INPUT_DEVICE = 0, + /** + * Output device role. + */ + OUTPUT_DEVICE = 1 +}; + +enum DeviceType { + /** + * Indicates device type none. + */ + DEVICE_TYPE_NONE = -1, + /** + * Indicates a speaker built in a device. + */ + SPEAKER = 0, + /** + * Indicates a headset, which is the combination of a pair of headphones and a microphone. + */ + WIRED_HEADSET = 1, + /** + * Indicates a Bluetooth device used for telephony. + */ + BLUETOOTH_SCO = 2, + /** + * Indicates a Bluetooth device supporting the Advanced Audio Distribution Profile (A2DP). + */ + BLUETOOTH_A2DP = 3, + /** + * Indicates a microphone built in a device. + */ + MIC = 4 +}; + + DeviceType getType(); + DeviceRole getRole(); + DeviceType deviceType_; + DeviceRole deviceRole_; + AudioDeviceDescriptor(); + virtual ~AudioDeviceDescriptor(); + bool Marshalling(Parcel &parcel) const override; + static AudioDeviceDescriptor* Unmarshalling(Parcel &parcel); +}; + +/** + * @brief The AudioSystemManager class is an abstract definition of audio manager. + * Provides a series of client/interfaces for audio management + */ + +class AudioSystemManager { +public: +enum AudioVolumeType { + /** + * Indicates audio streams of voices in calls. + */ + STREAM_VOICE_CALL = 1, + /** + * Indicates audio streams for system sounds. + */ + STREAM_SYSTEM = 2, + /** + * Indicates audio streams for ringtones. + */ + STREAM_RING = 3, + /** + * Indicates audio streams for music playback. + */ + STREAM_MUSIC = 4, + /** + * Indicates audio streams for alarms. + */ + STREAM_ALARM = 5, + /** + * Indicates audio streams for notifications. + */ + STREAM_NOTIFICATION = 6, + /** + * Indicates audio streams for voice calls routed through a connected Bluetooth device. + */ + STREAM_BLUETOOTH_SCO = 7, + /** + * Indicates audio streams for dual-tone multi-frequency (DTMF) tones. + */ + STREAM_DTMF = 8, + /** + * Indicates audio streams exclusively transmitted through the speaker (text-to-speech) of a device. + */ + STREAM_TTS = 9, + /** + * Indicates audio streams used for prompts in terms of accessibility. + */ + STREAM_ACCESSIBILITY = 10 + }; + static AudioSystemManager* GetInstance(); + int32_t SetVolume(AudioSystemManager::AudioVolumeType volumeType, float volume) const; + float GetVolume(AudioSystemManager::AudioVolumeType volumeType) const; + float GetMaxVolume(AudioSystemManager::AudioVolumeType volumeType) const; + float GetMinVolume(AudioSystemManager::AudioVolumeType volumeType) const; + int32_t SetMute(AudioSystemManager::AudioVolumeType volumeType, bool mute) const; + bool IsStreamMute(AudioSystemManager::AudioVolumeType volumeType) const; + int32_t SetMicrophoneMute(bool isMute) const; + bool IsMicrophoneMute(void) const; + std::vector> GetDevices(AudioDeviceDescriptor::DeviceFlag deviceFlag) const; + const std::string GetAudioParameter(const std::string key) const; + void SetAudioParameter(const std::string key, const std::string value) const; + int32_t SetDeviceActive(AudioDeviceDescriptor::DeviceType deviceType, bool flag) const; + bool IsDeviceActive(AudioDeviceDescriptor::DeviceType deviceType) const; + bool IsStreamActive(AudioSystemManager::AudioVolumeType volumeType) const; + bool SetRingerMode(AudioRingerMode ringMode) const; + AudioRingerMode GetRingerMode() const; +private: + AudioSystemManager(); + virtual ~AudioSystemManager(); + void init(); +}; +} // namespace AudioStandard +} // namespace OHOS +#endif // ST_AUDIO_SYSTEM_MANAGER_H diff --git a/interfaces/innerkits/native/audiopolicy/BUILD.gn b/interfaces/innerkits/native/audiopolicy/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..73aaa5789715966169c7f0ebad3618697dd74662 --- /dev/null +++ b/interfaces/innerkits/native/audiopolicy/BUILD.gn @@ -0,0 +1,96 @@ +# Copyright (C) 2021 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") + +config("audio_policy_public_config") { + include_dirs = [ + "//foundation/multimedia/audio_standard/services/include", + "//foundation/multimedia/audio_standard/services/include/client", + "//foundation/distributedschedule/safwk/services/safwk/include", + "//foundation/multimedia/audio_standard/frameworks/innerkitsimpl/common/include", + "//foundation/multimedia/audio_standard/interfaces/innerkits/native/audiocommon/include", + "//foundation/multimedia/audio_standard/interfaces/innerkits/native/audiopolicy/include", + "//foundation/multimedia/audio_standard/interfaces/innerkits/native/audiomanager/include", + "//foundation/multimedia/audio_standard/services/src/audio_policy/server/service/common", + "//foundation/multimedia/audio_standard/services/src/audio_policy/server/service/interface", + "//foundation/multimedia/audio_standard/services/src/audio_policy/server/service/manager", + "//foundation/multimedia/audio_standard/services/src/audio_policy/server/service/config", + "//foundation/multimedia/audio_standard/services/src/audio_policy/server/service", + "//foundation/multimedia/audio_standard/services/include/audio_policy/common", + "//foundation/multimedia/audio_standard/services/include/audio_policy/server", + "//foundation/multimedia/audio_standard/services/include/audio_policy/client", + "//foundation/multimedia/audio_standard/frameworks/innerkitsimpl/common/include", + "//foundation/communication/ipc/interfaces/innerkits/ipc_core/include", + "//foundation/multimedia/image/mock/native/include/foundation/multimedia/image/mock/native/include", + "//foundation/distributedschedule/samgr/interfaces/innerkits/samgr_proxy/include", + "//base/hiviewdfx/hilog/interfaces/native/innerkits/include", + "//utils/system/safwk/native/include", + "//utils/native/base/include", + "//third_party/bounds_checking_function/include", + ] + + cflags = [ + "-Wall", + "-Werror", + ] + + if (target_cpu == "arm") { + cflags += [ "-DBINDER_IPC_32BIT" ] + } +} + +ohos_shared_library("audio_policy_client") { + install_enable = true + sources = [ + "//foundation/multimedia/audio_standard/services/src/audio_policy/client/audio_policy_proxy.cpp", + "//foundation/multimedia/audio_standard/services/src/audio_policy/client/audio_policy_manager.cpp", + ] + + public_configs = [ + ":audio_policy_public_config", + ] + + deps = [ + "//utils/native/base:utils", + ] + + external_deps = [ + "ipc:ipc_core", + "samgr_L2:samgr_proxy", + "safwk:system_ability_fwk", + "hiviewdfx_hilog_native:libhilog", + ] + subsystem_name = "multimedia" + part_name = "multimedia_audio_standard" +} + +ohos_executable("audio_policy_test") { + install_enable = false + + sources = [ "//foundation/multimedia/audio_standard/services/test/audio_policy_test.cpp" ] + + configs = [ + ":audio_policy_public_config", + ] + + deps = [ + "//foundation/multimedia/audio_standard/interfaces/innerkits/native/audiomanager:audio_client", + "//third_party/bounds_checking_function:libsec_static", + ] + + external_deps = [ "hiviewdfx_hilog_native:libhilog" ] + + part_name = "multimedia_audio_standard" + subsystem_name = "multimedia" +} diff --git a/interfaces/innerkits/native/audiopolicy/include/audio_policy_manager.h b/interfaces/innerkits/native/audiopolicy/include/audio_policy_manager.h new file mode 100644 index 0000000000000000000000000000000000000000..cf677eefea328ae51309c0136118ed8aa4aabd9b --- /dev/null +++ b/interfaces/innerkits/native/audiopolicy/include/audio_policy_manager.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2021 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 ST_AUDIO_POLICY_MANAGER_H +#define ST_AUDIO_POLICY_MANAGER_H + +#include +#include "audio_info.h" + +namespace OHOS { +namespace AudioStandard { +class AudioPolicyManager { +public: + static AudioPolicyManager& GetInstance() + { + static AudioPolicyManager policyManager; + return policyManager; + } + + int32_t SetStreamVolume(AudioStreamType streamType, float volume); + + float GetStreamVolume(AudioStreamType streamType); + + int32_t SetStreamMute(AudioStreamType streamType, bool mute); + + bool GetStreamMute(AudioStreamType streamType); + + bool IsStreamActive(AudioStreamType streamType); + + int32_t SetDeviceActive(DeviceType deviceType, bool active); + + bool IsDeviceActive(DeviceType deviceType); + + int32_t SetRingerMode(AudioRingerMode ringMode); + + AudioRingerMode GetRingerMode(); +private: + AudioPolicyManager() + { + Init(); + } + ~AudioPolicyManager() {} + + void Init(); +}; +} // namespce AudioStandard +} // namespace OHOS + +#endif // ST_AUDIO_POLICY_MANAGER_H diff --git a/interfaces/innerkits/native/audiorecorder/BUILD.gn b/interfaces/innerkits/native/audiorecorder/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..12029f91354fe7f5d07bdb59ac8984fbb2b3139f --- /dev/null +++ b/interfaces/innerkits/native/audiorecorder/BUILD.gn @@ -0,0 +1,92 @@ +# Copyright (C) 2021 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") + +pulseaudio_dir = "//third_party/pulseaudio" + +group("audio_recorder_test_packages") { + deps = [ + ":audio_recorder_test", + ] +} + +config("audio_recorder_config") { + include_dirs = [ + "//foundation/multimedia/audio_standard/interfaces/innerkits/native/audiorecorder/include", + "//foundation/multimedia/audio_standard/frameworks/innerkitsimpl/audiorecorder/include", + "//foundation/multimedia/audio_standard/frameworks/innerkitsimpl/common/include", + "//foundation/multimedia/audio_standard/interfaces/innerkits/native/audiocommon/include", + "//foundation/multimedia/audio_standard/interfaces/innerkits/native/audiostream/include", + "//foundation/multimedia/audio_standard/interfaces/innerkits/native/audiosession/include", + "//foundation/multimedia/audio_standard/services/include", + "//foundation/multimedia/audio_standard/services/include/client", + "$pulseaudio_dir/src", + "$pulseaudio_dir/confgure/src", + ] + + cflags = [ + "-Wall", + "-Werror", + ] + +} + +ohos_shared_library("audio_recorder") { + install_enable = true + + configs = [ + ":audio_recorder_config", + ] + + sources = [ + "//foundation/multimedia/audio_standard/frameworks/innerkitsimpl/audiorecorder/src/audio_recorder.cpp", + ] + + deps = [ + "//foundation/multimedia/audio_standard/interfaces/innerkits/native/audiomanager:audio_client", + ] + + public_configs = [ ":audio_external_library_config" ] + + part_name = "multimedia_audio_standard" + subsystem_name = "multimedia" +} + +config("audio_external_library_config") { + include_dirs = [ "//foundation/multimedia/audio_standard/interfaces/innerkits/native/audiorecorder/include" ] +} + +ohos_executable("audio_recorder_test") { + + install_enable = true + + sources = [ + "//foundation/multimedia/audio_standard/services/test/audio_recorder_test.cpp", + ] + + configs = [ + ":audio_recorder_config", + ] + + deps = [ + "//foundation/multimedia/audio_standard/interfaces/innerkits/native/audiorecorder:audio_recorder", + ] + + external_deps = [ + "hiviewdfx_hilog_native:libhilog", + ] + + part_name = "multimedia_audio_standard" + subsystem_name = "multimedia" +} diff --git a/interfaces/innerkits/native/audiorecorder/include/audio_recorder.h b/interfaces/innerkits/native/audiorecorder/include/audio_recorder.h new file mode 100644 index 0000000000000000000000000000000000000000..f557908842850e08a93beb7e647bac9a225ad6a0 --- /dev/null +++ b/interfaces/innerkits/native/audiorecorder/include/audio_recorder.h @@ -0,0 +1,205 @@ +/* + * Copyright (C) 2021 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_RECORDER_H +#define AUDIO_RECORDER_H + +#include + +#include "audio_info.h" +#include "timestamp.h" + +namespace OHOS { +namespace AudioStandard { +/** + * @brief Defines information about audio record parameters + */ +struct AudioRecorderParams { + /** Audio source type */ + AudioSourceType inputSource = AUDIO_MIC; + /** Audio codec format */ + AudioEncodingType audioEncoding = ENCODING_PCM; + /** Sampling rate */ + AudioSamplingRate samplingRate = SAMPLE_RATE_44100; + /** Number of audio channels */ + AudioChannel audioChannel = MONO; + /** Audio stream type */ + AudioStreamType streamType = STREAM_MEDIA; + /** audioSampleFormat */ + AudioSampleFormat audioSampleFormat = SAMPLE_S16LE; +}; + +/** + * @brief Enumerates the recording states of the current device. + */ +enum RecorderState { + /** Create new recorder instance */ + RECORDER_NEW, + /** Recorder Prepared state */ + RECORDER_PREPARED, + /** Recorder Running state */ + RECORDER_RUNNING, + /** Recorder Stopped state */ + RECORDER_STOPPED, + /** Recorder Released state */ + RECORDER_RELEASED, + /** Recorder INVALID state */ + RECORDER_INVALID +}; + +/** + * @brief Provides functions for applications to implement audio recording. + */ +class AudioRecorder { +public: + /** + * @brief creater recorder instance. + */ + static std::unique_ptr Create(AudioStreamType audioStreamType); + + /** + * @brief Obtains the number of frames required in the current condition, in bytes per sample. + * + * @param frameCount Indicates the pointer in which framecount will be written + * @return Returns {@link SUCCESS} if frameCount is successfully obtained; returns an error code + * defined in {@link audio_errors.h} otherwise. + */ + virtual int32_t GetFrameCount(uint32_t &frameCount) const = 0; + + /** + * @brief Sets audio record parameters. + * + * @param params Indicates information about audio record parameters to set. For details, see + * {@link AudioRecorderParams}. + * @return Returns {@link SUCCESS} if the setting is successful; returns an error code defined + * in {@link audio_errors.h} otherwise. + */ + virtual int32_t SetParams(const AudioRecorderParams params) const = 0; + + /** + * @brief Obtains audio recorder parameters. + * + * This function can be called after {@link SetParams} is successful. + * + * @param params Indicates information about audio recorder parameters.For details,see + * {@link AudioRecorderParams}. + * @return Returns {@link SUCCESS} if the parameter information is successfully obtained; returns an error code + * defined in {@link audio_errors.h} otherwise. + */ + virtual int32_t GetParams(AudioRecorderParams ¶ms) const = 0; + + /** + * @brief Starts audio recording. + * + * @return Returns true if the recording is successfully started; returns false otherwise. + */ + virtual bool Start() const = 0; + + /** + * @brief record audio data. + * + * @param buffer Indicates the pointer to the buffer into which the audio data is to be written. + * @param userSize Indicates the size of the buffer into which the audio data is to be written, in bytes. + * userSize >= frameCount * channelCount * BytesPerSample must evaluate to true. You can call + * {@link GetFrameCount} to obtain the frameCount value. + * @param isBlockingRead Specifies whether data reading will be blocked. + * @return Returns the size of the audio data read from the device. The value ranges from 0 to + * userSize. If the reading fails, one of the following error codes is returned. + * ERR_INVALID_PARAM: The input parameter is incorrect. + * ERR_ILLEGAL_STATE: The AudioRecorder instance is not initialized. + * ERR_INVALID_READ: The read size < 0. + */ + virtual int32_t Read(uint8_t &buffer, size_t userSize, bool isBlockingRead) const = 0; + + /** + * @brief Obtains the audio record state. + * + * @return Returns the audio record state defined in {@link RecorderState}. + */ + virtual RecorderState GetStatus() const = 0; + + /** + * @brief Obtains the Timestamp. + * + * @param timestamp Indicates a {@link Timestamp} instance reference provided by the caller. + * @param base Indicates the time base, which can be {@link Timestamp.Timestampbase#BOOTTIME} or + * {@link Timestamp.Timestampbase#MONOTONIC}. + * @return Returns true if the timestamp is successfully obtained; returns false otherwise. + */ + virtual bool GetAudioTime(Timestamp ×tamp, Timestamp::Timestampbase base) const = 0; + + /** + * @brief Stops audio recording. + * + * @return Returns true if the recording is successfully stopped; returns false otherwise. + */ + virtual bool Stop() const = 0; + /** + * @brief flush record stream. + * + * @return Returns true if the object is successfully flushed; returns false otherwise. + */ + virtual bool Flush() const = 0; + + /** + * @brief Releases a local AudioRecorder object. + * + * @return Returns true if the object is successfully released; returns false otherwise. + */ + virtual bool Release() const = 0; + + /** + * @brief Obtains a reasonable minimum buffer size for recorder, however, the recorder can + * accept other read sizes as well. + * + * @param bufferSize Indicates a buffersize pointer value that wil be written. + * @return Returns {@link SUCCESS} if bufferSize is successfully obtained; returns an error code + * defined in {@link audio_errors.h} otherwise. + */ + virtual int32_t GetBufferSize(size_t &bufferSize) const = 0; + + /** + * @brief Obtains the recorder supported formats. + * + * @return vector with recorder supported formats. + */ + static std::vector GetSupportedFormats(); + + /** + * @brief Obtains the recorder supported channels. + * + * @return vector with recorder supported channels. + */ + static std::vector GetSupportedChannels(); + + /** + * @brief Obtains the recorder supported encoding types. + * + * @return vector with recorder supported encoding types. + */ + static std::vector GetSupportedEncodingTypes(); + + /** + * @brief Obtains the recorder supported SupportedSamplingRates. + * + * @return vector with recorder supported SupportedSamplingRates. + */ + static std::vector GetSupportedSamplingRates(); + + virtual ~AudioRecorder(); +}; +} // namespace AudioStandard +} // namespace OHOS +#endif // AUDIO_RECORDER_H diff --git a/interfaces/innerkits/native/audiorenderer/BUILD.gn b/interfaces/innerkits/native/audiorenderer/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..26cb43c9ecde6c5c27adf959d63974c6b221d090 --- /dev/null +++ b/interfaces/innerkits/native/audiorenderer/BUILD.gn @@ -0,0 +1,72 @@ +# Copyright (C) 2021 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") + +pulseaudio_dir = "//third_party/pulseaudio" + +config("audio_renderer_config") { + include_dirs = [ + "//foundation/multimedia/audio_standard/interfaces/innerkits/native/audiorecorder/include", + "//foundation/multimedia/audio_standard/frameworks/innerkitsimpl/audiorenderer/include", + "//foundation/multimedia/audio_standard/frameworks/innerkitsimpl/common/include", + "//foundation/multimedia/audio_standard/interfaces/innerkits/native/audiocommon/include", + "//foundation/multimedia/audio_standard/interfaces/innerkits/native/audiostream/include", + "//foundation/multimedia/audio_standard/interfaces/innerkits/native/audiosession/include", + "//foundation/multimedia/audio_standard/services/include", + "//foundation/multimedia/audio_standard/services/include/client", + "$pulseaudio_dir/src", + "$pulseaudio_dir/confgure/src", + ] + + cflags = [ + "-Wall", + "-Werror", + ] + +} + +ohos_shared_library("audio_renderer") { + install_enable = true + + configs = [ ":audio_renderer_config" ] + + sources = [ "//foundation/multimedia/audio_standard/frameworks/innerkitsimpl/audiorenderer/src/audio_renderer.cpp" ] + + deps = [ "//foundation/multimedia/audio_standard/interfaces/innerkits/native/audiomanager:audio_client" ] + + public_configs = [ ":audio_external_library_config" ] + + part_name = "multimedia_audio_standard" + subsystem_name = "multimedia" +} + +ohos_executable("audio_renderer_test") { + + install_enable = true + + sources = [ "//foundation/multimedia/audio_standard/services/test/audio_renderer_test.cpp" ] + + configs = [ ":audio_renderer_config" ] + + deps = [ ":audio_renderer" ] + + external_deps = [ "hiviewdfx_hilog_native:libhilog" ] + + part_name = "multimedia_audio_standard" + subsystem_name = "multimedia" +} + +config("audio_external_library_config") { + include_dirs = [ "//foundation/multimedia/audio_standard/interfaces/innerkits/native/audiorenderer/include" ] +} diff --git a/interfaces/innerkits/native/audiorenderer/include/audio_renderer.h b/interfaces/innerkits/native/audiorenderer/include/audio_renderer.h new file mode 100644 index 0000000000000000000000000000000000000000..77fbf7595ad05227fc156b7292915a17c3920bcb --- /dev/null +++ b/interfaces/innerkits/native/audiorenderer/include/audio_renderer.h @@ -0,0 +1,227 @@ +/* + * Copyright (C) 2021 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_RENDERER_H +#define AUDIO_RENDERER_H + +#include + +#include "audio_info.h" +#include "timestamp.h" + +namespace OHOS { +namespace AudioStandard { +/** + * @brief Defines information about audio renderer parameters. + */ + +struct AudioRendererParams { + /** Sample Format */ + AudioSampleFormat sampleFormat = SAMPLE_S16LE; + /** Sampling rate */ + AudioSamplingRate sampleRate = SAMPLE_RATE_8000; + /** Number of channels */ + AudioChannel channelCount = MONO; + /** Encoding Type */ + AudioEncodingType encodingType = ENCODING_PCM; +}; + +/** + * @brief Enumerates the rendering states of the current device. + */ +enum RendererState { + /** Create New Renderer instance */ + RENDERER_NEW, + /** Reneder Prepared state */ + RENDERER_PREPARED, + /** Rendere Running state */ + RENDERER_RUNNING, + /** Renderer Stopped state */ + RENDERER_STOPPED, + /** Renderer Released state */ + RENDERER_RELEASED, + /** INVALID state */ + RENDERER_INVALID +}; + +/** + * @brief Provides functions for applications to implement audio rendering. + */ +class AudioRenderer { +public: + /** + * @brief creater renderer instance. + * + * @param audioStreamType The audio streamtype to be created. + * refer AudioStreamType in audio_info.h. + * @return Returns unique pointer to the AudioRenderer object + */ + static std::unique_ptr Create(AudioStreamType audioStreamType); + /** + * @brief Obtains the number of frames required in the current condition, in bytes per sample. + * + * @param frameCount Indicates the reference variable in which framecount will be written + * @return Returns {@link SUCCESS} if frameCount is successfully obtained; returns an error code + * defined in {@link audio_errors.h} otherwise. + */ + virtual int32_t GetFrameCount(uint32_t &frameCount) const = 0; + + /** + * @brief Sets audio renderer parameters. + * + * @param params Indicates information about audio renderer parameters to set. For details, see + * {@link AudioRendererParams}. + * @return Returns {@link SUCCESS} if the setting is successful; returns an error code defined + * in {@link audio_errors.h} otherwise. + */ + virtual int32_t SetParams(const AudioRendererParams params) const = 0; + + /** + * @brief Obtains audio renderer parameters. + * + * This function can be called after {@link SetParams} is successful. + * + * @param params Indicates information about audio renderer parameters. For details, see + * {@link AudioRendererParams}. + * @return Returns {@link SUCCESS} if the parameter information is successfully obtained; returns an error code + * defined in {@link audio_errors.h} otherwise. + */ + virtual int32_t GetParams(AudioRendererParams ¶ms) const = 0; + + /** + * @brief Starts audio rendering. + * + * @return Returns true if the rendering is successfully started; returns false otherwise. + */ + virtual bool Start() const = 0; + + /** + * @brief Writes audio data. + * + * @param buffer Indicates the pointer to the buffer which contains the audio data to be written. + * @param bufferSize Indicates the size of the buffer which contains audio data to be written, in bytes. + * @return Returns the size of the audio data written to the device. The value ranges from 0 to + * bufferSize. If the write fails, one of the following error codes is returned. + * ERR_INVALID_PARAM: The input parameter is incorrect. + * ERR_ILLEGAL_STATE: The AudioRenderer instance is not initialized. + * ERR_INVALID_WRITE: The written audio data size is < 0. + * ERR_WRITE_FAILED: The audio data write failed . + */ + virtual int32_t Write(uint8_t *buffer, size_t bufferSize) = 0; + + /** + * @brief Obtains the audio renderer state. + * + * @return Returns the audio renderer state defined in {@link RendererState}. + */ + virtual RendererState GetStatus() const = 0; + + /** + * @brief Obtains the timestamp. + * + * @param timestamp Indicates a {@link Timestamp} instance reference provided by the caller. + * @param base Indicates the time base, which can be {@link Timestamp.Timestampbase#BOOTTIME} or + * {@link Timestamp.Timestampbase#MONOTONIC}. + * @return Returns true if the timestamp is successfully obtained; returns false otherwise. + */ + virtual bool GetAudioTime(Timestamp ×tamp, Timestamp::Timestampbase base) const = 0; + + /** + * @brief Obtains the latency in microseconds. + * + * @param latency Indicates the reference variable into which latency value will be written. + * @return Returns {@link SUCCESS} if latency is successfully obtained, returns an error code + * defined in {@link audio_errors.h} otherwise. + */ + virtual int32_t GetLatency(uint64_t &latency) const = 0; + + /** + * @brief drain renderer buffer. + * + * @return Returns true if the buffer is successfully drained; returns false otherwise. + */ + virtual bool Drain() const = 0; + + /** + * @brief flush renderer stream. + * + * @return Returns true if the object is successfully flushed; returns false otherwise. + */ + virtual bool Flush() const = 0; + + /** + * @brief Pauses audio rendering. + * + * @return Returns true if the rendering is successfully Paused; returns false otherwise. + */ + virtual bool Pause() const = 0; + + /** + * @brief Stops audio rendering. + * + * @return Returns true if the rendering is successfully stopped; returns false otherwise. + */ + virtual bool Stop() const = 0; + + /** + * @brief Releases a local AudioRenderer object. + * + * @return Returns true if the object is successfully released; returns false otherwise. + */ + virtual bool Release() const = 0; + + /** + * @brief Obtains a reasonable minimum buffer size for rendering, however, the renderer can + * accept other write sizes as well. + * + * @param bufferSize Indicates the reference variable into which buffer size value wil be written. + * @return Returns {@link SUCCESS} if bufferSize is successfully obtained; returns an error code + * defined in {@link audio_errors.h} otherwise. + */ + virtual int32_t GetBufferSize(size_t &bufferSize) const = 0; + + /** + * @brief Obtains the foramts supported by renderer. + * + * @return Returns vector with supported formats. + */ + static std::vector GetSupportedFormats(); + + /** + * @brief Obtains the SupportedSamplingRates supported by renderer. + * + * @return Returns vector with supported SupportedSamplingRates. + */ + static std::vector GetSupportedSamplingRates(); + + /** + * @brief Obtains the channels supported by renderer. + * + * @return Returns vector with supported channels. + */ + static std::vector GetSupportedChannels(); + + /** + * @brief Obtains the encoding types supported by renderer. + * + * @return Returns vector with supported encoding types. + */ + static std::vector GetSupportedEncodingTypes(); + + virtual ~AudioRenderer(); +}; +} // namespace AudioStandard +} // namespace OHOS +#endif // AUDIO_RENDERER_H diff --git a/interfaces/innerkits/native/audiosession/include/audio_session.h b/interfaces/innerkits/native/audiosession/include/audio_session.h new file mode 100644 index 0000000000000000000000000000000000000000..08b32e9b00bc9e41dce56bd702203cc2e8064326 --- /dev/null +++ b/interfaces/innerkits/native/audiosession/include/audio_session.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2021 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_SESSION_H +#define AUDIO_SESSION_H + +#include "audio_service_client.h" + +namespace OHOS { +namespace AudioStandard { +enum SessionType { + SESSION_CONTROL, + SESSION_PLAYBACK, + SESSION_RECORD +}; + +struct AudioDevDescriptor { +}; + +class AudioSession : public AudioServiceClient { +public: + uint32_t GetSessionID(); + + AudioDevDescriptor* GetActiveAudioSinkDevice(uint32_t sessionID); + AudioDevDescriptor* GetActiveAudioSourceDevice(uint32_t sessionID); + + bool SetActiveAudioSinkDevice(uint32_t sessionID, const AudioDevDescriptor& audioDesc); + bool SetActiveAudioSourceDevice(uint32_t sessionID, const AudioDevDescriptor& audioDesc); + float GetAudioStreamVolume(uint32_t sessionID); + float GetAudioDeviceVolume(uint32_t sessionID); + bool SetAudioStreamVolume(uint32_t sessionID, float volume); + bool SetAudioDeviceVolume(uint32_t sessionID, float volume); + +private: + AudioSession* CreateSession(SessionType eSession); +}; +} // namespace AudioStandard +} // namespace OHOS +#endif // AUDIO_SESSION_H diff --git a/interfaces/innerkits/native/audiostream/include/audio_stream.h b/interfaces/innerkits/native/audiostream/include/audio_stream.h new file mode 100644 index 0000000000000000000000000000000000000000..7907b50b2f035d8f4a2237a2f7031c3f8ab0f56a --- /dev/null +++ b/interfaces/innerkits/native/audiostream/include/audio_stream.h @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2021 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_session.h" +#include "timestamp.h" + +#ifndef AUDIO_STREAM_H +#define AUDIO_STREAM_H + +namespace OHOS { +namespace AudioStandard { +enum AudioMode { + AUDIO_MODE_PLAYBACK, + AUDIO_MODE_RECORD +}; + +/** + * @brief Enumerates the stream states of the current device. + * + * @since 1.0 + * @version 1.0 + */ +enum State { + /** New */ + NEW, + /** Prepared */ + PREPARED, + /** Running */ + RUNNING, + /** Paused */ + PAUSED, + /** Stopped */ + STOPPED, + /** Released */ + RELEASED, + /** INVALID */ + INVALID +}; + +class AudioStream : public AudioSession { +public: + AudioStream(AudioStreamType eStreamType, AudioMode eMode); + virtual ~AudioStream(); + + int32_t SetAudioStreamInfo(const AudioStreamParams info); + int32_t GetAudioStreamInfo(AudioStreamParams &info); + + uint32_t GetAudioSessionID(); + State GetState(); + bool GetAudioTime(Timestamp ×tamp, Timestamp::Timestampbase base); + int32_t GetBufferSize(size_t &bufferSize); + int32_t GetFrameCount(uint32_t &frameCount); + int32_t GetLatency(uint64_t &latency); + + std::vector GetSupportedFormats(); + std::vector GetSupportedChannels(); + std::vector GetSupportedEncodingTypes(); + std::vector GetSupportedSamplingRates(); + + // Common APIs + bool StartAudioStream(); + bool PauseAudioStream(); + bool StopAudioStream(); + bool ReleaseAudioStream(); + bool FlushAudioStream(); + + // Playback related APIs + bool DrainAudioStream(); + size_t Write(uint8_t *buffer, size_t buffer_size); + + // Recorder related APIs + int32_t Read(uint8_t &buffer, size_t userSize, bool isBlockingRead); +private: + AudioStreamType eStreamType_; + AudioMode eMode_; + State state_; + std::atomic isReadInProgress_; + std::atomic isWriteInProgress_; +}; +} // namespace AudioStandard +} // namespace OHOS +#endif // AUDIO_STREAM_H diff --git a/interfaces/kits/js/audio_manager/@ohos.multimedia.audio.d.ts b/interfaces/kits/js/audio_manager/@ohos.multimedia.audio.d.ts old mode 100755 new mode 100644 index 2deecd5ab08ddb62406565ab3ea498f96021f64c..4ed98a3abce1ef683d98030a36282c82925229e4 --- a/interfaces/kits/js/audio_manager/@ohos.multimedia.audio.d.ts +++ b/interfaces/kits/js/audio_manager/@ohos.multimedia.audio.d.ts @@ -12,199 +12,349 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -import {ErrorCallback, AsyncCallback} from './basic'; +import {ErrorCallback, AsyncCallback, Callback} from './basic'; +import {VideoPlayer, AudioPlayer} from './@ohos.multimedia.media' /** * @name audio * @since 6 * @sysCap SystemCapability.Multimedia.Audio - * @import import audio from '@ohos.multimedia.audio'; + * @import import audio from '@ohos.Multimedia.audio'; * @permission */ declare namespace audio { + /** + * Obtains an AudioManager instance. + * @devices + * @sysCap SystemCapability.Multimedia.Audio + */ + function getAudioManager(): AudioManager; + + /** + * Enumerates audio stream types. + * @devices + * @sysCap SystemCapability.Multimedia.Audio + */ + enum AudioVolumeType { + /** + * Audio streams for ring tones + */ + RINGTONE = 2, + /** + * Audio streams for media purpose + */ + MEDIA = 3, + } + + /** + * Enumerates audio device flags. + * @devices + * @sysCap SystemCapability.Multimedia.Audio + */ + enum DeviceFlag { + /** + * Output devices + */ + OUTPUT_DEVICES_FLAG = 1, + /** + * Input devices + */ + INPUT_DEVICES_FLAG = 2, + /** + * All devices + */ + ALL_DEVICES_FLAG = 3, + } + /** + * Enumerates device roles. + * @devices + * @sysCap SystemCapability.Multimedia.Audio + */ + enum DeviceRole { + /** + * Input role + */ + INPUT_DEVICE = 1, + /** + * Output role + */ + OUTPUT_DEVICE = 2, + } + /** + * Enumerates device types. + * @devices + * @sysCap SystemCapability.Multimedia.Audio + */ + enum DeviceType { + /** + * Invalid device + */ + INVALID = 0, + /** + * Speaker + */ + SPEAKER = 1, + /** + * Wired headset + */ + WIRED_HEADSET = 2, + /** + * Bluetooth device using the synchronous connection oriented link (SCO) + */ + BLUETOOTH_SCO = 3, + /** + * Bluetooth device using advanced audio distribution profile (A2DP) + */ + BLUETOOTH_A2DP = 4, + /** + * Microphone + */ + MIC = 5, + } + /** + * Enumerates Audio Ringer modes + * @devices + * @sysCap SystemCapability.Multimedia.Audio + */ + enum AudioRingMode { + /** + * Silent mode + */ + RINGER_MODE_SILENT = 0, + /** + * Vibration mode + */ + RINGER_MODE_VIBRATE, + /** + * Normal mode + */ + RINGER_MODE_NORMAL, + } + + /** + * Manages audio volume and audio device information. + * @devices + * @sysCap SystemCapability.Multimedia.Audio + */ + interface AudioManager { /** - * get the audiomanager of the audio + * Sets volume for a stream. This method uses an asynchronous callback to return the execution result. * @devices * @sysCap SystemCapability.Multimedia.Audio */ - function getAudioManager(): AudioManager; - + setVolume(volumeType: AudioVolumeType, volume: number, callback: AsyncCallback): void; /** - * the type of audio stream + * Sets volume for a stream. This method uses a promise to return the execution result. * @devices * @sysCap SystemCapability.Multimedia.Audio */ - enum AudioVolumeType { - /** - * the media stream - */ - MEDIA = 1, - /** - * the ringtone stream - */ - RINGTONE = 2, - } - + setVolume(volumeType: AudioVolumeType, volume: number): Promise; /** - * the flag type of device - * @devices - * @sysCap SystemCapability.Multimedia.Audio - */ - enum DeviceFlag { - /** - * the device flag of output - */ - OUTPUT_DEVICES_FLAG = 1, - /** - * the device flag of input - */ - INPUT_DEVICES_FLAG = 2, - /** - * the device flag of all devices - */ - ALL_DEVICES_FLAG = 3, - } - /** - * the role of device - * @devices - * @sysCap SystemCapability.Multimedia.Audio - */ - enum DeviceRole { - /** - * the role of input devices - */ - INPUT_DEVICE = 1, - /** - * the role of output devices - */ - OUTPUT_DEVICE = 2, - } - /** - * the type of device - * @devices - * @sysCap SystemCapability.Multimedia.Audio - */ - enum DeviceType { - /** - * invalid - */ - INVALID = 0, - /** - * speaker - */ - SPEAKER = 1, - /** - * wired headset - */ - WIRED_HEADSET = 2, - /** - * bluetooth sco - */ - BLUETOOTH_SCO = 3, - /** - * bluetooth a2dp - */ - BLUETOOTH_A2DP = 4, - /** - * mic - */ - MIC = 5, - } - /** - * the audiomanager of the audio - * @devices - * @sysCap SystemCapability.Multimedia.Audio - */ - interface AudioManager { - /** - * set the volume of the audiovolumetype - * @devices - * @sysCap SystemCapability.Multimedia.Audio - */ - setVolume(audioType: AudioVolumeType,volume: number,callback: AsyncCallback): void; - /** - * set the volume of the audiovolumetype - * @devices - * @sysCap SystemCapability.Multimedia.Audio - */ - setVolume(audioType: AudioVolumeType,volume: number): Promise; - /** - * get the volume of the audiovolumetype - * @devices - * @sysCap SystemCapability.Multimedia.Audio - */ - getVolume(audioType: AudioVolumeType, callback: AsyncCallback): void; - /** - * get the volume of the audiovolumetype - * @devices - * @sysCap SystemCapability.Multimedia.Audio - */ - getVolume(audioType: AudioVolumeType): Promise; - /** - * get the min volume of the audiovolumetype - * @devices - * @sysCap SystemCapability.Multimedia.Audio - */ - getMinVolume(audioType: AudioVolumeType, callback: AsyncCallback): void - /** - * get the min volume of the audiovolumetype - * @devices - * @sysCap SystemCapability.Multimedia.Audio - */ - getMinVolume(audioType: AudioVolumeType): Promise; - /** - * get the max volume of the audiovolumetype - * @devices - * @sysCap SystemCapability.Multimedia.Audio - */ - getMaxVolume(audioType: AudioVolumeType, callback: AsyncCallback): void - /** - * get the max volume of the audiovolumetype - * @devices - * @sysCap SystemCapability.Multimedia.Audio - */ - getMaxVolume(audioType: AudioVolumeType): Promise; - /** - * get the device list of the audio devices by the audio flag - * @devices - * @sysCap SystemCapability.Multimedia.Audio - */ - getDevices(deviceFlag: DeviceFlag, callback: AsyncCallback): void; - /** - * get the device list of the audio devices by the audio flag - * @devices - * @sysCap SystemCapability.Multimedia.Audio - */ - getDevices(deviceFlag: DeviceFlag): Promise; - } - + * Obtains volume of a stream. This method uses an asynchronous callback to return the execution result. + * @devices + * @sysCap SystemCapability.Multimedia.Audio + */ + getVolume(volumeType: AudioVolumeType, callback: AsyncCallback): void; /** - * the Descriptor of the device + * Obtains the volume of a stream. This method uses a promise to return the execution result. * @devices * @sysCap SystemCapability.Multimedia.Audio */ - interface AudioDeviceDescriptor { - /** - * the role of device - * @devices - * @sysCap SystemCapability.Multimedia.Audio - */ - readonly deviceRole: DeviceRole; - /** - * the type of device - * @devices - * @sysCap SystemCapability.Multimedia.Audio - */ - readonly deviceType: DeviceType; - } - + getVolume(volumeType: AudioVolumeType): Promise; + /** + * Obtains the minimum volume allowed for a stream. This method uses an asynchronous callback to return the execution result. + * @devices + * @sysCap SystemCapability.Multimedia.Audio + */ + getMinVolume(volumeType: AudioVolumeType, callback: AsyncCallback): void; + /** + * Obtains the minimum volume allowed for a stream. This method uses a promise to return the execution result. + * @devices + * @sysCap SystemCapability.Multimedia.Audio + */ + getMinVolume(volumeType: AudioVolumeType): Promise; + /** + * Obtains the maximum volume allowed for a stream. This method uses an asynchronous callback to return the execution result. + * @devices + * @sysCap SystemCapability.Multimedia.Audio + */ + getMaxVolume(volumeType: AudioVolumeType, callback: AsyncCallback): void; + /** + * Obtains the maximum volume allowed for a stream. This method uses a promise to return the execution result. + * @devices + * @sysCap SystemCapability.Multimedia.Audio + */ + getMaxVolume(volumeType: AudioVolumeType): Promise; + /** + * Sets the stream to mute. This method uses an asynchronous callback to return the execution result. + * @sysCap SystemCapability.Multimedia.Audio + * @devices + */ + mute(volumeType: AudioVolumeType, mute: boolean, callback: AsyncCallback): void; + /** + * Sets the stream to mute. This method uses a promise to return the execution result. + * @devices + * @sysCap SystemCapability.Multimedia.Audio + */ + mute(volumeType: AudioVolumeType, mute: boolean): Promise; + /** + * Checks whether the stream is muted. This method uses an asynchronous callback to return the execution result. + * @devices + * @sysCap SystemCapability.Multimedia.Audio + */ + isMute(volumeType: AudioVolumeType, callback: AsyncCallback): void; /** - * the Descriptor list of the devices + * Checks whether the stream is muted. This method uses a promise to return the execution result. + * @devices + * @sysCap SystemCapability.Multimedia.Audio + */ + isMute(volumeType: AudioVolumeType): Promise; + /** + * Checks whether the stream is active. This method uses an asynchronous callback to return the execution result. + * @devices + * @sysCap SystemCapability.Multimedia.Audio + */ + isActive(volumeType: AudioVolumeType, callback: AsyncCallback): void; + /** + * Checks whether the stream is active. This method uses a promise to return the execution result. + * @devices + * @sysCap SystemCapability.Multimedia.Audio + */ + isActive(volumeType: AudioVolumeType): Promise; + /** + * Mute/Unmutes the microphone. This method uses an asynchronous callback to return the execution result. + * @devices + * @sysCap SystemCapability.Multimedia.Audio + */ + setMicrophoneMute(mute: boolean, callback: AsyncCallback): void; + /** + * Mute/Unmutes the microphone. This method uses a promise to return the execution result. + * @devices + * @sysCap SystemCapability.Multimedia.Audio + */ + setMicrophoneMute(mute: boolean): Promise; + /** + * Checks whether the microphone is muted. This method uses an asynchronous callback to return the execution result. + * @devices + * @sysCap SystemCapability.Multimedia.Audio + */ + isMicrophoneMute(callback: AsyncCallback): void; + /** + * Checks whether the microphone is muted. This method uses a promise to return the execution result. + * @devices + * @sysCap SystemCapability.Multimedia.Audio + */ + isMicrophoneMute(): Promise; + /** + * Sets the ringer mode. This method uses an asynchronous callback to return the execution result. + * @devices + * @sysCap SystemCapability.Multimedia.Audio + */ + setRingerMode(mode: AudioRingMode, callback: AsyncCallback): void; + /** + * Sets the ringer mode. This method uses a promise to return the execution result. + * @devices + * @sysCap SystemCapability.Multimedia.Audio + */ + setRingerMode(mode: AudioRingMode): Promise; + /** + * Gets the ringer mode. This method uses an asynchronous callback to return the execution result. + * @devices + * @sysCap SystemCapability.Multimedia.Audio + */ + getRingerMode(callback: AsyncCallback): void; + /** + * Gets the ringer mode. This method uses a promise to return the execution result. + * @devices + * @sysCap SystemCapability.Multimedia.Audio + */ + getRingerMode(): Promise; + /** + * Sets the audio parameter. This method uses an asynchronous callback to return the execution result. + * @devices + * @sysCap SystemCapability.Multimedia.Audio + */ + setAudioParameter(key: string, value: string, callback: AsyncCallback): void; + /** + * Sets the audio parameter. This method uses a promise to return the execution result. + * @devices + * @sysCap SystemCapability.Multimedia.Audio + */ + setAudioParameter(key: string, value: string): Promise; + /** + * Gets the audio parameter. This method uses an asynchronous callback to return the execution result. + * @devices + * @sysCap SystemCapability.Multimedia.Audio + */ + getAudioParameter(key: string, callback: AsyncCallback): void; + /** + * Gets the audio parameter. This method uses a promise to return the execution result. + * @devices + * @sysCap SystemCapability.Multimedia.Audio + */ + getAudioParameter(key: string): Promise; + /** + * Obtains the audio devices of a specified flag. This method uses an asynchronous callback to return the execution result. * @devices * @sysCap SystemCapability.Multimedia.Audio */ - type AudioDeviceDescriptors = Array>; + getDevices(deviceFlag: DeviceFlag, callback: AsyncCallback): void; + /** + * Obtains the audio devices with a specified flag. This method uses a promise to return the execution result. + * @devices + * @sysCap SystemCapability.Multimedia.Audio + */ + getDevices(deviceFlag: DeviceFlag): Promise; + /** + * Activates the device. This method uses an asynchronous callback to return the execution result. + * @devices + * @sysCap SystemCapability.Multimedia.Audio + */ + setDeviceActive(deviceType: DeviceType, active: boolean, callback: AsyncCallback): void; + /** + * Activates the device. This method uses a promise to return the execution result. + * @sysCap SystemCapability.Multimedia.Audio + * @devices + */ + setDeviceActive(deviceType: DeviceType, active: boolean): Promise; + /** + * Checks whether the device is active. This method uses an asynchronous callback to return the execution result. + * @sysCap SystemCapability.Multimedia.Audio + * @devices + */ + isDeviceActive(deviceType: DeviceType, callback: AsyncCallback): void; + /** + * Checks whether the device is active. This method uses a promise to return the execution result. + * @sysCap SystemCapability.Multimedia.Audio + * @devices + */ + isDeviceActive(deviceType: DeviceType): Promise; + } + + /** + * Describes an audio device. + * @devices + * @sysCap SystemCapability.Multimedia.Audio + */ + interface AudioDeviceDescriptor { + /** + * Audio device role + * @devices + */ + readonly deviceRole: DeviceRole; + /** + * Audio device type + * @devices + */ + readonly deviceType: DeviceType; + } + + /** + * A queue of AudioDeviceDescriptor, which is read-only. + * @devices + * @sysCap SystemCapability.Multimedia.Audio + */ + type AudioDeviceDescriptors = Array>; } export default audio; \ No newline at end of file diff --git a/interfaces/kits/js/audio_manager/include/audio_device_descriptor_napi.h b/interfaces/kits/js/audio_manager/include/audio_device_descriptor_napi.h old mode 100755 new mode 100644 index 54e25d7b785c4527930634704574043f074ddf36..9b9bcbb86467ff6db7f4b0407bcd0e61c81ff590 --- a/interfaces/kits/js/audio_manager/include/audio_device_descriptor_napi.h +++ b/interfaces/kits/js/audio_manager/include/audio_device_descriptor_napi.h @@ -18,14 +18,19 @@ #include #include -#include "audio_device_descriptor.h" +#include "audio_system_manager.h" #include "napi/native_api.h" #include "napi/native_node_api.h" +namespace OHOS { +namespace AudioStandard { static const std::string AUDIO_DEVICE_DESCRIPTOR_NAPI_CLASS_NAME = "AudioDeviceDescriptor"; class AudioDeviceDescriptorNapi { public: + AudioDeviceDescriptorNapi(); + ~AudioDeviceDescriptorNapi(); + enum DeviceType { INVALID = 0, SPEAKER = 1, @@ -42,22 +47,21 @@ public: static napi_value Init(napi_env env, napi_value exports); static napi_value CreateAudioDeviceDescriptorWrapper(napi_env env, - OHOS::sptr audioDeviceDescriptor); + sptr deviceDescriptor); private: - explicit AudioDeviceDescriptorNapi(); - ~AudioDeviceDescriptorNapi(); - static void Destructor(napi_env env, void* nativeObject, void* finalize_hint); static napi_value Construct(napi_env env, napi_callback_info info); static napi_value GetDeviceRole(napi_env env, napi_callback_info info); static napi_value GetDeviceType(napi_env env, napi_callback_info info); static napi_ref sConstructor_; - static OHOS::sptr sAudioDescriptor_; + static sptr sAudioDescriptor_; - OHOS::sptr audioDescriptor_; + sptr audioDescriptor_; napi_env env_; napi_ref wrapper_; }; +} // namespace AudioStandard +} // namespace OHOS #endif /* AUDIO_DEVICE_DESCRIPTOR_NAPI_H_ */ diff --git a/interfaces/kits/js/audio_manager/include/audio_manager_napi.h b/interfaces/kits/js/audio_manager/include/audio_manager_napi.h old mode 100755 new mode 100644 index 39bf7f028fd1555fc30e6b9954c7ba96dbbb7762..416e59bbe246870afb3e5e75ede58f8a8c313324 --- a/interfaces/kits/js/audio_manager/include/audio_manager_napi.h +++ b/interfaces/kits/js/audio_manager/include/audio_manager_napi.h @@ -18,17 +18,22 @@ #include #include -#include "audio_svc_manager.h" +#include "audio_system_manager.h" #include "napi/native_api.h" #include "napi/native_node_api.h" +namespace OHOS { +namespace AudioStandard { static const std::string AUDIO_MNGR_NAPI_CLASS_NAME = "AudioManager"; class AudioManagerNapi { public: + AudioManagerNapi(); + ~AudioManagerNapi(); + enum AudioVolumeType { - MEDIA = 1, - RINGTONE = 2 + RINGTONE = 2, + MEDIA = 3 }; enum DeviceFlag { @@ -37,12 +42,15 @@ public: ALL_DEVICES_FLAG = 3 }; + enum AudioRingMode { + RINGER_MODE_SILENT = 0, + RINGER_MODE_VIBRATE, + RINGER_MODE_NORMAL + }; + static napi_value Init(napi_env env, napi_value exports); private: - explicit AudioManagerNapi(); - ~AudioManagerNapi(); - static void Destructor(napi_env env, void* nativeObject, void* finalize_hint); static napi_value Construct(napi_env env, napi_callback_info info); static napi_value CreateAudioManagerWrapper(napi_env env); @@ -52,21 +60,36 @@ private: static napi_value GetMaxVolume(napi_env env, napi_callback_info info); static napi_value GetMinVolume(napi_env env, napi_callback_info info); static napi_value GetDevices(napi_env env, napi_callback_info info); + static napi_value SetStreamMute(napi_env env, napi_callback_info info); + static napi_value IsStreamMute(napi_env env, napi_callback_info info); + static napi_value IsStreamActive(napi_env env, napi_callback_info info); + static napi_value SetRingerMode(napi_env env, napi_callback_info info); + static napi_value GetRingerMode(napi_env env, napi_callback_info info); + static napi_value SetDeviceActive(napi_env env, napi_callback_info info); + static napi_value IsDeviceActive(napi_env env, napi_callback_info info); + static napi_value SetAudioParameter(napi_env env, napi_callback_info info); + static napi_value GetAudioParameter(napi_env env, napi_callback_info info); + static napi_value SetMicrophoneMute(napi_env env, napi_callback_info info); + static napi_value IsMicrophoneMute(napi_env env, napi_callback_info info); - static napi_status AddNamedProperty(napi_env env, napi_value object, const char *name, int32_t enumValue); + static napi_status AddNamedProperty(napi_env env, napi_value object, const std::string name, int32_t enumValue); static napi_value CreateAudioVolumeTypeObject(napi_env env); static napi_value CreateDeviceFlagObject(napi_env env); static napi_value CreateDeviceRoleObject(napi_env env); static napi_value CreateDeviceTypeObject(napi_env env); + static napi_value CreateAudioRingModeObject(napi_env env); static napi_ref sConstructor_; static napi_ref audioVolumeTypeRef_; static napi_ref deviceFlagRef_; static napi_ref deviceRoleRef_; static napi_ref deviceTypeRef_; + static napi_ref audioRingModeRef_; - OHOS::AudioSvcManager *audioMngr_; + AudioSystemManager *audioMngr_; napi_env env_; napi_ref wrapper_; }; +} // namespace AudioStandard +} // namespace OHOS #endif /* AUDIO_MNGR_NAPI_H_ */ diff --git a/ohos.build b/ohos.build old mode 100755 new mode 100644 index 29ab46795d7e7c6b8317bfdd0995d7d09d62a1d6..7c5667c56b4f856c8a0b97308647fbeba8fc83cf --- a/ohos.build +++ b/ohos.build @@ -7,10 +7,19 @@ ], "module_list": [ "//foundation/multimedia/audio_standard/services:audio_service", - "//foundation/multimedia/audio_standard/services:audio_service.rc", + "//foundation/multimedia/audio_standard/services:pulseaudio.rc", "//foundation/multimedia/audio_standard/sa_profile:audio_service_sa_profile", "//foundation/multimedia/audio_standard/interfaces/kits/js/audio_manager:audio", - "//foundation/multimedia/audio_standard/interfaces/kits/js/audio_manager:audio_js" + "//foundation/multimedia/audio_standard/interfaces/kits/js/audio_manager:audio_js", + "//foundation/multimedia/audio_standard/frameworks/innerkitsimpl/pulseaudio:pulseaudio_packages", + "//foundation/multimedia/audio_standard/interfaces/innerkits/native/audiomanager:audio_client_test_packages", + "//foundation/multimedia/audio_standard/interfaces/innerkits/native/audiorecorder:audio_recorder_test_packages", + "//foundation/multimedia/audio_standard/interfaces/innerkits/native/audiorenderer:audio_renderer_test", + "//foundation/multimedia/audio_standard/services:audio_policy_service_packages", + "//foundation/multimedia/audio_standard/sa_profile:audio_policy_service_sa_profile", + "//foundation/multimedia/audio_standard/interfaces/innerkits/native/audiopolicy:audio_policy_test", + "//foundation/multimedia/audio_standard/frameworks/innerkitsimpl/audiocapturer:audio_capturer_source", + "//foundation/multimedia/audio_standard/frameworks/innerkitsimpl/audiorenderer:audio_renderer_sink" ], "inner_kits": [ { @@ -18,9 +27,17 @@ "name": "//foundation/multimedia/audio_standard/interfaces/innerkits/native/audiomanager:audio_client", "header": { "header_files": [ - "audio_svc_manager.h" + "audio_system_manager.h", + "audio_session.h", + "audio_stream.h", + "audio_info.h" ], - "header_base": "//foundation/multimedia/audio_standard/interfaces/innerkits/native/audiomanager/include" + "header_base": [ + "//foundation/multimedia/audio_standard/interfaces/innerkits/native/audiomanager/include", + "//foundation/multimedia/audio_standard/interfaces/innerkits/native/audiosession/include", + "//foundation/multimedia/audio_standard/interfaces/innerkits/native/audiostream/include", + "//foundation/multimedia/audio_standard/interfaces/innerkits/native/audiocommon/include" + ] } }, { @@ -33,6 +50,31 @@ "header_base": "//foundation/multimedia/audio_standard/interfaces/innerkits/native/audiocapturer/include" } }, + { + "type": "none", + "name": "//foundation/multimedia/audio_standard/interfaces/innerkits/native/audiorecorder:audio_recorder", + "header": { + "header_files": [ + "audio_recorder.h", + "audio_info.h" + ], + "header_base": [ + "//foundation/multimedia/audio_standard/interfaces/innerkits/native/audiorecorder/include", + "//foundation/multimedia/audio_standard/interfaces/innerkits/native/audiocommon/include" + ] + } + }, + { + "type": "none", + "name": "//foundation/multimedia/audio_standard/interfaces/innerkits/native/audiorenderer:audio_renderer", + "header": { + "header_files": [ + "audio_renderer.h", + "audio_info.h" + ], + "header_base": "//foundation/multimedia/audio_standard/interfaces/innerkits/native/audiorenderer/include" + } + }, { "type": "none", "name": "//foundation/multimedia/audio_standard/interfaces/kits/js/audio_manager:audio", @@ -44,6 +86,9 @@ "header_base": "//foundation/multimedia/audio_standard/interfaces/kits/js/audio_manager/include" } } + ], + "test_list": [ + "//foundation/multimedia/audio_standard/frameworks/innerkitsimpl/test:audio_test" ] } } diff --git a/sa_profile/3001.xml b/sa_profile/3001.xml old mode 100755 new mode 100644 index 6c8be70c77fb07f123bdabf6ad97e968c1043565..a7a5f6a5a4dc521e8a46223e8ef9b535d89f4105 --- a/sa_profile/3001.xml +++ b/sa_profile/3001.xml @@ -14,7 +14,7 @@ limitations under the License. --> - audio_service + pulseaudio /system/lib/libaudio_service.z.so diff --git a/sa_profile/3009.xml b/sa_profile/3009.xml new file mode 100644 index 0000000000000000000000000000000000000000..aeed3091e73d1c86eb7b6ad481a0baf4a0f31404 --- /dev/null +++ b/sa_profile/3009.xml @@ -0,0 +1,28 @@ + + + + audio_policy + + /system/lib/libaudio_policy_service.z.so + + + 3009 + /system/lib/libaudio_policy_service.z.so + true + false + 1 + + diff --git a/sa_profile/BUILD.gn b/sa_profile/BUILD.gn index f284c01d86c58fe5f108443d3c9424d7ffb3eafd..d1d4f423798d7bd61fe4fc735e105f10dbf3609d 100644 --- a/sa_profile/BUILD.gn +++ b/sa_profile/BUILD.gn @@ -18,3 +18,9 @@ ohos_sa_profile("audio_service_sa_profile") { part_name = "multimedia_audio_standard" } + +ohos_sa_profile("audio_policy_service_sa_profile") { + sources = [ "3009.xml" ] + + part_name = "multimedia_audio_standard" +} diff --git a/services/BUILD.gn b/services/BUILD.gn old mode 100755 new mode 100644 index 210d594f886ac6b9dca25d4dbd18d1ead2a72008..259fc18f65d35d83e448d4d302edb7670c4308c9 --- a/services/BUILD.gn +++ b/services/BUILD.gn @@ -12,12 +12,17 @@ # limitations under the License. import("//build/ohos.gni") +AUDIO_POLICY_SERVER_DIR = "//foundation/multimedia/audio_standard/services/src/audio_policy/server" -ohos_prebuilt_etc("audio_service.rc") { - source = "etc/audio_service.rc" - relative_install_dir = "init" - part_name = "multimedia_audio_standard" - subsystem_name = "multimedia" +ohos_prebuilt_etc("pulseaudio.rc") { + if (use_musl) { + source = "etc/pulseaudio.cfg" + } else { + source = "etc/pulseaudio.rc" + } + relative_install_dir = "init" + part_name = "multimedia_audio_standard" + subsystem_name = "multimedia" } config("audio_service_config") { @@ -27,6 +32,9 @@ config("audio_service_config") { "include", "include/server", "//foundation/multimedia/audio_standard/frameworks/innerkitsimpl/common/include", + "//base/hiviewdfx/hilog/interfaces/native/innerkits/include", + "//foundation/multimedia/audio_standard/frameworks/innerkitsimpl/audiocapturer/include", + "//drivers/peripheral/audio/interfaces/include" ] if (target_cpu == "arm") { @@ -45,17 +53,118 @@ ohos_shared_library("audio_service") { configs = [ ":audio_service_config" ] deps = [ - "//foundation/multimedia/audio_standard/interfaces/innerkits/native/audiomanager:audio_client", - "//utils/native/base:utils", + "//utils/native/base:utils", + "//foundation/multimedia/audio_standard/interfaces/innerkits/native/audiomanager:audio_client", + "//foundation/multimedia/audio_standard/frameworks/innerkitsimpl/pulseaudio/src/daemon:pulseaudio", + "//foundation/multimedia/audio_standard/frameworks/innerkitsimpl/audiocapturer:audio_capturer_source", ] external_deps = [ - "hiviewdfx_hilog_native:libhilog", "ipc:ipc_core", + "samgr_L2:samgr_proxy", "safwk:system_ability_fwk", + "hiviewdfx_hilog_native:libhilog", + ] + + subsystem_name = "multimedia" + part_name = "multimedia_audio_standard" +} + +group("audio_policy_service_packages") { + deps = [ + ":audio_policy.rc", + ":audio_policy_service", + ":audio_policy_config", + ] +} + +ohos_prebuilt_etc("audio_policy.rc") { + if (use_musl) { + source = "etc/audio_policy.cfg" + } else { + source = "etc/audio_policy.rc" + } + relative_install_dir = "init" + part_name = "multimedia_audio_standard" + subsystem_name = "multimedia" +} + +config("audio_policy_public_config") { + include_dirs = [ + "//foundation/multimedia/audio_standard/services/include", + "//foundation/multimedia/audio_standard/services/include/client", + "//foundation/distributedschedule/safwk/services/safwk/include", + "//foundation/multimedia/audio_standard/frameworks/innerkitsimpl/common/include", + "//foundation/multimedia/audio_standard/interfaces/innerkits/native/audiocommon/include", + "//foundation/multimedia/audio_standard/interfaces/innerkits/native/audiomanager/include", + "//foundation/multimedia/audio_standard/services/src/audio_policy/server/service/include/common", + "//foundation/multimedia/audio_standard/services/src/audio_policy/server/service/include/interface", + "//foundation/multimedia/audio_standard/services/src/audio_policy/server/service/include/manager", + "//foundation/multimedia/audio_standard/services/src/audio_policy/server/service/include/config", + "//foundation/multimedia/audio_standard/services/src/audio_policy/server/service/include", + "//foundation/multimedia/audio_standard/services/include/audio_policy/common", + "//foundation/multimedia/audio_standard/services/include/audio_policy/server", + "//foundation/multimedia/audio_standard/services/include/audio_policy/client", + "//foundation/multimedia/audio_standard/frameworks/innerkitsimpl/common/include", + "//foundation/communication/ipc/interfaces/innerkits/ipc_core/include", + "//foundation/distributedschedule/samgr/interfaces/innerkits/samgr_proxy/include", + "//foundation/distributeddatamgr/distributeddatamgr/interfaces/innerkits/distributeddata/include", + "//base/hiviewdfx/hilog/interfaces/native/innerkits/include", + "//third_party/libxml2/include", + "//third_party/pulseaudio/src", + "//third_party/pulseaudio/confgure/src", + "//foundation/multimedia/audio_standard/frameworks/innerkitsimpl/pulseaudio/include", + "//utils/system/safwk/native/include", + "//utils/native/base/include", + "//third_party/bounds_checking_function/include", + ] + + cflags = [ + "-Wall", + "-Werror", + ] + + if (target_cpu == "arm") { + cflags += [ "-DBINDER_IPC_32BIT" ] + } +} + +ohos_shared_library("audio_policy_service") { + install_enable = true + sources = [ + "//foundation/multimedia/audio_standard/services/src/audio_policy/server/audio_policy_server.cpp", + "//foundation/multimedia/audio_standard/services/src/audio_policy/server/audio_policy_manager_stub.cpp", + "//foundation/multimedia/audio_standard/services/src/audio_policy/server/service/src/audio_policy_service.cpp", + "//foundation/multimedia/audio_standard/services/src/audio_policy/server/service/src/manager/pulseaudio_policy_manager.cpp", + "//foundation/multimedia/audio_standard/services/src/audio_policy/server/service/src/config/xml_parser.cpp", + ] + + public_configs = [ + ":audio_policy_public_config", + ] + + deps = [ + "//utils/native/base:utils", + "//foundation/multimedia/audio_standard/frameworks/innerkitsimpl/pulseaudio/src/pulse:pulse", + "//third_party/libxml2:xml2", + "//foundation/distributeddatamgr/distributeddatamgr/interfaces/innerkits/distributeddata:distributeddata_inner", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/adapter:distributeddata_adapter", + ] + + external_deps = [ + "ipc:ipc_core", "samgr_L2:samgr_proxy", + "safwk:system_ability_fwk", + "hiviewdfx_hilog_native:libhilog", ] + subsystem_name = "multimedia" + part_name = "multimedia_audio_standard" +} + +ohos_prebuilt_etc("audio_policy_config") { + source = "$AUDIO_POLICY_SERVER_DIR/etc/audio_policy_config.xml" subsystem_name = "multimedia" + module_install_dir = "etc/audio" part_name = "multimedia_audio_standard" } diff --git a/services/etc/audio_policy.cfg b/services/etc/audio_policy.cfg new file mode 100644 index 0000000000000000000000000000000000000000..f76aaaefefa795569e90d6ad6ae08480433dd967 --- /dev/null +++ b/services/etc/audio_policy.cfg @@ -0,0 +1,15 @@ +{ + "jobs" : [{ + "name" : "audio_policy_start", + "cmds" : [ + "start audio_policy" + ] + } + ], + "services" : [{ + "name" : "audio_policy", + "path" : ["/system/bin/sa_main", "/system/profile/audio_policy.xml"], + "disabled" : 1 + } + ] +} diff --git a/services/etc/audio_service.rc b/services/etc/audio_policy.rc similarity index 81% rename from services/etc/audio_service.rc rename to services/etc/audio_policy.rc index 3cb4b53f293090cd3d5bd828452bcaba21a40bcc..9e9f3f594aac5411dafe870a7db636e48ace6ba8 100644 --- a/services/etc/audio_service.rc +++ b/services/etc/audio_policy.rc @@ -11,12 +11,11 @@ # See the License for the specific language governing permissions and # limitations under the License. -service audio_service /system/bin/sa_main /system/profile/audio_service.xml +service audio_policy /system/bin/sa_main /system/profile/audio_policy.xml class z_core - user audioserver - group system shell seclabel u:r:audiodistributedservice:s0 + disabled -on boot - start audio_service +on audio_policy_start + start audio_policy diff --git a/services/etc/pulseaudio.cfg b/services/etc/pulseaudio.cfg new file mode 100644 index 0000000000000000000000000000000000000000..d444809b5399315509e2e41f975c0867961e3c64 --- /dev/null +++ b/services/etc/pulseaudio.cfg @@ -0,0 +1,22 @@ +{ + "jobs" : [{ + "name" : "post-fs-data", + "cmds" : [ + "mkdir /data/local/.pulse_dir", + "chmod 755 /data/local/.pulse_dir", + "chown system shell /data/local/.pulse_dir", + "export PULSE_STATE_PATH /data/local/.pulse_dir", + "export PULSE_RUNTIME_PATH /data/local/.pulse_dir", + "start pulseaudio", + "exec /system/bin/sleep 4", + "trigger audio_policy_start" + ] + } + ], + "services" : [{ + "name" : "pulseaudio", + "path" : ["/system/bin/sa_main", "/system/profile/pulseaudio.xml"], + "disabled" : 1 + } + ] +} diff --git a/services/etc/pulseaudio.rc b/services/etc/pulseaudio.rc new file mode 100644 index 0000000000000000000000000000000000000000..cec5b2a6d8f83ee97975873771ddd96485837b75 --- /dev/null +++ b/services/etc/pulseaudio.rc @@ -0,0 +1,27 @@ +# Copyright (C) 2021 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. + +service pulseaudio /system/bin/sa_main /system/profile/pulseaudio.xml + class z_core + seclabel u:r:audiodistributedservice:s0 + disabled + +on post-fs-data + mkdir /data/local/.pulse_dir + chmod 755 /data/local/.pulse_dir + chown system shell /data/local/.pulse_dir + export PULSE_STATE_PATH "/data/local/.pulse_dir" + export PULSE_RUNTIME_PATH "/data/local/.pulse_dir" + start pulseaudio + exec /system/bin/sleep 4 + trigger audio_policy_start diff --git a/services/include/audio_error.h b/services/include/audio_error.h new file mode 100644 index 0000000000000000000000000000000000000000..0a4a5f24317f53084e6ce2799e42b48cff51e138 --- /dev/null +++ b/services/include/audio_error.h @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2021 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_ERROR_H +#define AUDIO_ERROR_H + +namespace OHOS { +namespace AudioStandard { +enum AudioServiceErrorCodes { +}; + +enum AudioServiceEventTypes { +}; +} // namespace AudioStandard +} // namespace OHOS +#endif // AUDIO_ERROR_H diff --git a/services/include/audio_manager_base.h b/services/include/audio_manager_base.h old mode 100755 new mode 100644 index aac47da54c05edfad5dd25be90343b2249518d09..2927bae0d9208082668bdc0880db437c00c29eb2 --- a/services/include/audio_manager_base.h +++ b/services/include/audio_manager_base.h @@ -20,38 +20,40 @@ #include "iremote_broker.h" #include "iremote_proxy.h" #include "iremote_stub.h" -#include "audio_svc_manager.h" +#include "audio_system_manager.h" namespace OHOS { +namespace AudioStandard { class IStandardAudioService : public IRemoteBroker { public: /** - * Set Volume. + * Obtains max volume. * - * @return Returns ERR_OK on success, others on failure. + * @return Returns the max volume. */ - virtual void SetVolume(AudioSvcManager::AudioVolumeType volumeType, int32_t volume) = 0; + virtual float GetMaxVolume(AudioSystemManager::AudioVolumeType volumeType) = 0; /** - * Obtains current volume. + * Obtains min volume. * - * @return Returns the current volume. + * @return Returns the min volume. */ - virtual int GetVolume(AudioSvcManager::AudioVolumeType volumeType) = 0; - + virtual float GetMinVolume(AudioSystemManager::AudioVolumeType volumeType) = 0; + /** - * Obtains max volume. + * Sets Microphone Mute status. * - * @return Returns the max volume. + * @param isMute Mute status true or false to be set. + * @return Returns 0 if success. Otherise returns Errocode defined in audio_errors.h. */ - virtual int GetMaxVolume(AudioSvcManager::AudioVolumeType volumeType) = 0; + virtual int32_t SetMicrophoneMute(bool isMute) = 0; - /** - * Obtains min volume. + /** + * Gets Microphone Mute status. * - * @return Returns the min volume. + * @return Returns true or false */ - virtual int GetMinVolume(AudioSvcManager::AudioVolumeType volumeType) = 0; + virtual bool IsMicrophoneMute() = 0; /** * Obtains device array. @@ -60,12 +62,31 @@ public: */ virtual std::vector> GetDevices(AudioDeviceDescriptor::DeviceFlag deviceFlag) = 0; + /** + * Set Audio Parameter. + * + * @param key for the audio parameter to be set + * @param value associated with the key for the audio parameter to be set + * @return none. + */ + virtual void SetAudioParameter(const std::string key, const std::string value) = 0; + + /** + * Get Audio Parameter. + * + * @param key for the audio parameter to be set + * @return Returns value associated to the key requested. + */ + virtual const std::string GetAudioParameter(const std::string key) = 0; + enum { - SET_VOLUME = 0, - GET_VOLUME = 1, - GET_MAX_VOLUME = 2, - GET_MIN_VOLUME = 3, - GET_DEVICES = 4, + GET_MAX_VOLUME = 0, + GET_MIN_VOLUME = 1, + GET_DEVICES = 2, + GET_AUDIO_PARAMETER = 3, + SET_AUDIO_PARAMETER = 4, + SET_MICROPHONE_MUTE = 5, + IS_MICROPHONE_MUTE = 6 }; public: @@ -78,5 +99,6 @@ public: MessageParcel &reply, MessageOption &option) override; bool IsPermissionValid(); }; +} // namespace AudioStandard } // namespace OHOS #endif // I_ST_AUDIO_MANAGER_BASE_H diff --git a/services/include/audio_policy/client/audio_policy_base.h b/services/include/audio_policy/client/audio_policy_base.h new file mode 100644 index 0000000000000000000000000000000000000000..1fd80b4ed080c330bdf030a7884b2c158c7b15cd --- /dev/null +++ b/services/include/audio_policy/client/audio_policy_base.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2021 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_AUDIO_POLICY_BASE_H +#define I_AUDIO_POLICY_BASE_H + +#include "audio_policy_types.h" +#include "ipc_types.h" +#include "iremote_broker.h" +#include "iremote_proxy.h" +#include "iremote_stub.h" + +namespace OHOS { +namespace AudioStandard { +class IAudioPolicy : public IRemoteBroker { +public: + + virtual int32_t SetStreamVolume(AudioStreamType streamType, float volume) = 0; + + virtual float GetStreamVolume(AudioStreamType streamType) = 0; + + virtual int32_t SetStreamMute(AudioStreamType streamType, bool mute) = 0; + + virtual bool GetStreamMute(AudioStreamType streamType) = 0; + + virtual bool IsStreamActive(AudioStreamType streamType) = 0; + + virtual int32_t SetDeviceActive(DeviceType deviceType, bool active) = 0; + + virtual bool IsDeviceActive(DeviceType deviceType) = 0; + + virtual int32_t SetRingerMode(AudioRingerMode ringMode) = 0; + + virtual AudioRingerMode GetRingerMode() = 0; +public: + DECLARE_INTERFACE_DESCRIPTOR(u"IAudioPolicy"); +}; +} // AudioStandard +} // namespace OHOS +#endif // I_AUDIO_POLICY_BASE_H diff --git a/services/include/audio_policy/client/audio_policy_manager_stub.h b/services/include/audio_policy/client/audio_policy_manager_stub.h new file mode 100644 index 0000000000000000000000000000000000000000..6bacdadbc03b698b674c78f6c3323a438ba4d5bf --- /dev/null +++ b/services/include/audio_policy/client/audio_policy_manager_stub.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2021 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_POLICY_MANAGER_STUB_H +#define AUDIO_POLICY_MANAGER_STUB_H + +#include "audio_policy_base.h" + +namespace OHOS { +namespace AudioStandard { +class AudioPolicyManagerStub : public IRemoteStub { +public: + virtual int32_t OnRemoteRequest(uint32_t code, MessageParcel &data, + MessageParcel &reply, MessageOption &option) override; + bool IsPermissionValid(); + +private: + void SetStreamVolumeInternal(MessageParcel &data, MessageParcel &reply); + void SetRingerModeInternal(MessageParcel &data, MessageParcel &reply); + void GetRingerModeInternal(MessageParcel &data); + void GetStreamVolumeInternal(MessageParcel &data, MessageParcel &reply); + void SetStreamMuteInternal(MessageParcel &data, MessageParcel &reply); + void GetStreamMuteInternal(MessageParcel &data, MessageParcel &reply); + void IsStreamActiveInternal(MessageParcel &data, MessageParcel &reply); + void SetDeviceActiveInternal(MessageParcel &data, MessageParcel &reply); + void IsDeviceActiveInternal(MessageParcel &data, MessageParcel &reply); +}; +} // AudioStandard +} // namespace OHOS +#endif // AUDIO_POLICY_MANAGER_STUB_H diff --git a/services/include/audio_policy/client/audio_policy_proxy.h b/services/include/audio_policy/client/audio_policy_proxy.h new file mode 100644 index 0000000000000000000000000000000000000000..913ee455ae4ea7d08ef4f1875691a62676fe6dae --- /dev/null +++ b/services/include/audio_policy/client/audio_policy_proxy.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2021 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 ST_AUDIO_POLICY_PROXY_H +#define ST_AUDIO_POLICY_PROXY_H + +#include "iremote_proxy.h" +#include "audio_policy_base.h" + +namespace OHOS { +namespace AudioStandard { +class AudioPolicyProxy : public IRemoteProxy { +public: + explicit AudioPolicyProxy(const sptr &impl); + virtual ~AudioPolicyProxy() = default; + + int32_t SetStreamVolume(AudioStreamType streamType, float volume) override; + + float GetStreamVolume(AudioStreamType streamType) override; + + int32_t SetStreamMute(AudioStreamType streamType, bool mute) override; + + bool GetStreamMute(AudioStreamType streamType) override; + + bool IsStreamActive(AudioStreamType streamType) override; + + int32_t SetDeviceActive(DeviceType deviceType, bool active) override; + + bool IsDeviceActive(DeviceType deviceType) override; + + int32_t SetRingerMode(AudioRingerMode ringMode) override; + + AudioRingerMode GetRingerMode() override; +private: + static inline BrokerDelegator mDdelegator; +}; +} // namespace AudioStandard +} // namespace OHOS +#endif // ST_AUDIO_POLICY_PROXY_H diff --git a/services/include/audio_policy/common/audio_policy_types.h b/services/include/audio_policy/common/audio_policy_types.h new file mode 100644 index 0000000000000000000000000000000000000000..d9c1a593887e15de68f124d7f356dda9646ab144 --- /dev/null +++ b/services/include/audio_policy/common/audio_policy_types.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2021 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 ST_AUDIO_POLICY_TYPES_H +#define ST_AUDIO_POLICY_TYPES_H + +#include + +namespace OHOS { +namespace AudioStandard { +enum AudioPolicyCommand { + SET_STREAM_VOLUME, + GET_STREAM_VOLUME, + SET_STREAM_MUTE, + GET_STREAM_MUTE, + IS_STREAM_ACTIVE, + SET_DEVICE_ACTIVE, + IS_DEVICE_ACTIVE, + SET_RINGER_MODE, + GET_RINGER_MODE +}; +} // namespace AudioStandard +} // namespace OHOS + +#endif // ST_AUDIO_POLICY_TYPES_H diff --git a/services/include/audio_policy/server/audio_policy_server.h b/services/include/audio_policy/server/audio_policy_server.h new file mode 100644 index 0000000000000000000000000000000000000000..290e35aa3d775f85d337b72486c52f8bf594fecd --- /dev/null +++ b/services/include/audio_policy/server/audio_policy_server.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2021 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 ST_AUDIO_POLICY_SERVER_H +#define ST_AUDIO_POLICY_SERVER_H + +#include +#include + +#include "audio_policy_manager_stub.h" +#include "audio_policy_service.h" +#include "iremote_stub.h" +#include "system_ability.h" + +namespace OHOS { +namespace AudioStandard { +class AudioPolicyServer : public SystemAbility, public AudioPolicyManagerStub { + DECLARE_SYSTEM_ABILITY(AudioPolicyServer); +public: + DISALLOW_COPY_AND_MOVE(AudioPolicyServer); + + explicit AudioPolicyServer(int32_t systemAbilityId, bool runOnCreate = true); + + virtual ~AudioPolicyServer() = default; + + void OnDump() override; + void OnStart() override; + void OnStop() override; + + int32_t SetStreamVolume(AudioStreamType streamType, float volume) override; + + float GetStreamVolume(AudioStreamType streamType) override; + + int32_t SetStreamMute(AudioStreamType streamType, bool mute) override; + + bool GetStreamMute(AudioStreamType streamType) override; + + bool IsStreamActive(AudioStreamType streamType) override; + + int32_t SetDeviceActive(DeviceType deviceType, bool active) override; + + bool IsDeviceActive(DeviceType deviceType) override; + + int32_t SetRingerMode(AudioRingerMode ringMode) override; + + AudioRingerMode GetRingerMode() override; + +private: + AudioPolicyService& mPolicyService; +}; +} // namespace AudioStandard +} // namespace OHOS +#endif // ST_AUDIO_POLICY_SERVER_H diff --git a/services/include/client/audio_manager_proxy.h b/services/include/client/audio_manager_proxy.h old mode 100755 new mode 100644 index 0b91a84a029af71321eebdce0fd939c2f0a31b3d..3b82f08d82be4bb23b2cfd3e90dcd401120fd27a --- a/services/include/client/audio_manager_proxy.h +++ b/services/include/client/audio_manager_proxy.h @@ -17,23 +17,25 @@ #define ST_AUDIO_MANAGER_PROXY_H #include "iremote_proxy.h" -#include "audio_svc_manager.h" +#include "audio_system_manager.h" #include "audio_manager_base.h" -#include "audio_device_descriptor.h" namespace OHOS { +namespace AudioStandard { class AudioManagerProxy : public IRemoteProxy { public: explicit AudioManagerProxy(const sptr &impl); virtual ~AudioManagerProxy() = default; - - void SetVolume(AudioSvcManager::AudioVolumeType volumeType, int32_t volume) override; - int32_t GetVolume(AudioSvcManager::AudioVolumeType volumeType) override; - int32_t GetMaxVolume(AudioSvcManager::AudioVolumeType volumeType) override; - int32_t GetMinVolume(AudioSvcManager::AudioVolumeType volumeType) override; + float GetMaxVolume(AudioSystemManager::AudioVolumeType volumeType) override; + float GetMinVolume(AudioSystemManager::AudioVolumeType volumeType) override; + int32_t SetMicrophoneMute(bool isMute) override; + bool IsMicrophoneMute() override; std::vector> GetDevices(AudioDeviceDescriptor::DeviceFlag deviceFlag) override; + const std::string GetAudioParameter(const std::string key) override; + void SetAudioParameter(const std::string key, const std::string value) override; private: static inline BrokerDelegator delegator_; }; +} // namespace AudioStandard } // namespace OHOS #endif // ST_AUDIO_MANAGER_PROXY_H diff --git a/services/include/client/audio_service_client.h b/services/include/client/audio_service_client.h new file mode 100644 index 0000000000000000000000000000000000000000..a9374d1df27e6b0c15e1a584a165f72e1d7297f6 --- /dev/null +++ b/services/include/client/audio_service_client.h @@ -0,0 +1,345 @@ +/* + * Copyright (C) 2021 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_SERVICE_CLIENT_H +#define AUDIO_SERVICE_CLIENT_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace OHOS { +namespace AudioStandard { +enum ASClientType { + AUDIO_SERVICE_CLIENT_PLAYBACK, + AUDIO_SERVICE_CLIENT_RECORD, + AUDIO_SERVICE_CLIENT_CONTROLLER +}; + +typedef pa_sink_input_info SinkInputInfo; +typedef pa_source_output_info SourceOutputInfo; +typedef pa_sink_info SinkDeviceInfo; +typedef pa_source_info SourceDeviceInfo; +typedef pa_client_info ClientInfo; + +struct StreamBuffer { + uint8_t *buffer; // the virtual address of stream + uint32_t bufferLen; // stream length in bytes +}; + +struct AudioCache { + std::unique_ptr buffer; + uint32_t readIndex; + uint32_t writeIndex; + uint32_t totalCacheSize; + bool isFull; +}; + +class AudioRendererCallbacks { +public: + virtual ~AudioRendererCallbacks(); + virtual void OnSinkDeviceUpdatedCb() const = 0; + // Need to check required state changes to update applications + virtual void OnStreamStateChangeCb() const = 0; + virtual void OnStreamBufferUnderFlowCb() const = 0; + virtual void OnStreamBufferOverFlowCb() const = 0; + virtual void OnErrorCb(AudioServiceErrorCodes error) const = 0; + virtual void OnEventCb(AudioServiceEventTypes error) const = 0; +}; + +class AudioRecorderCallbacks { +public: + virtual ~AudioRecorderCallbacks(); + virtual void OnSourceDeviceUpdatedCb() const = 0; + // Need to check required state changes to update applications + virtual void OnStreamStateChangeCb() const = 0; + virtual void OnStreamBufferUnderFlowCb() const = 0; + virtual void OnStreamBufferOverFlowCb() const = 0; + virtual void OnErrorCb(AudioServiceErrorCodes error) const = 0; + virtual void OnEventCb(AudioServiceEventTypes error) const = 0; +}; + +class AudioServiceClient { +public: + AudioServiceClient(); + virtual ~AudioServiceClient(); + + /** + * Initializes audio service client for the required client type + * + * @param eClientType indicates the client type like playback, record or controller. + * @return Returns {@code 0} if success; returns {@code -1} otherwise. + */ + int32_t Initialize(ASClientType eClientType); + + // Stream handling APIs + + /** + * Creates & initializes resources based on the audioParams and audioType + * + * @param audioParams indicate format, sampling rate and number of channels + * @param audioType indicate the stream type like music, system, ringtone etc + * @return Returns {@code 0} if success; returns {@code -1} otherwise. + */ + int32_t CreateStream(AudioStreamParams audioParams, AudioStreamType audioType); + + /** + * Starts the stream created using CreateStream + * + * @return Returns {@code 0} if success; returns {@code -1} otherwise. + */ + int32_t StartStream(); + + /** + * Stops the stream created using CreateStream + * + * @return Returns {@code 0} if success; returns {@code -1} otherwise. + */ + int32_t StopStream(); + + /** + * Flushes the stream created using CreateStream. This is applicable for + * playback only + * + * @return Returns {@code 0} if success; returns {@code -1} otherwise. + */ + int32_t FlushStream(); + + /** + * Drains the stream created using CreateStream. This is applicable for + * playback only + * + * @return Returns {@code 0} if success; returns {@code -1} otherwise. + */ + int32_t DrainStream(); + + /** + * Pauses the stream + * + * @return Returns {@code 0} if success; returns {@code -1} otherwise. + */ + int32_t PauseStream(); + + /** + * Sets the volume of the stream associated with session ID + * + * @param sessionID indicates the ID for the active stream to be controlled + * @param volume indicates volume level between 0 to 65536 + * @return Returns {@code 0} if success; returns {@code -1} otherwise. + */ + int32_t SetStreamVolume(uint32_t sessionID, uint32_t volume); + + /** + * Get the volume of the stream associated with session ID + * + * @param sessionID indicates the ID for the active stream to be controlled + * @return returns volume level between 0 to 65536 + */ + uint32_t GetStreamVolume(uint32_t sessionID); + + /** + * Writes audio data of the stream created using CreateStream to active sink device + * + * @param buffer contains audio data to write + * @param bufferSize indicates the size of audio data in bytes to write from the buffer + * @param pError indicates pointer to error which will be filled in case of internal errors + * @return returns size of audio data written in bytes. + */ + size_t WriteStream(const StreamBuffer &stream, int32_t &pError); + + /** + * Reads audio data of the stream created using CreateStream from active source device + * + * @param StreamBuffer including buffer to be filled with audio data + * and bufferSize indicating the size of audio data to read into buffer + * @param isBlocking indicates if the read is blocking or not + * @return Returns size read if success; returns {@code -1} failure. + */ + int32_t ReadStream(StreamBuffer &stream, bool isBlocking); + + /** + * Release the resources allocated using CreateStream + * + * @return Returns {@code 0} if success; returns {@code -1} otherwise. + */ + int32_t ReleaseStream(); + + /** + * Provides the current timestamp for playback/record stream created using CreateStream + * + * @param timeStamp will be filled up with current timestamp + * @return Returns {@code 0} if success; returns {@code -1} otherwise. + */ + int32_t GetCurrentTimeStamp(uint64_t &timeStamp); + + /** + * Provides the current latency for playback/record stream created using CreateStream + * + * @param latency will be filled up with the current latency in microseconds + * @return Returns {@code 0} if success; returns {@code -1} otherwise. + */ + int32_t GetAudioLatency(uint64_t &latency); + + /** + * Provides the playback/record stream parameters created using CreateStream + * + * @param audioParams will be filled up with stream audio parameters + * @return Returns {@code 0} if success; returns {@code -1} otherwise. + */ + int32_t GetAudioStreamParams(AudioStreamParams& audioParams); + + /** + * Provides the minimum buffer size required for this audio stream + * created using CreateStream + * @param minBufferSize will be set to minimum buffer size in bytes + * @return Returns {@code 0} if success; returns {@code -1} otherwise. + */ + int32_t GetMinimumBufferSize(size_t &minBufferSize); + + /** + * Provides the minimum frame count required for this audio stream + * created using CreateStream + * @param frameCount will be set to minimum number of frames + * @return Returns {@code 0} if success; returns {@code -1} otherwise. + */ + int32_t GetMinimumFrameCount(uint32_t &frameCount); + + /** + * Provides the sampling rate for the active audio stream + * created using CreateStream + * + * @return Returns sampling rate in Hz + */ + uint32_t GetSamplingRate(); + + /** + * Provides the channel count for the active audio stream + * created using CreateStream + * + * @return Returns number of channels + */ + uint8_t GetChannelCount(); + + /** + * Provides the sample size for the active audio stream + * created using CreateStream + * + * @return Returns sample size in number of bits + */ + uint8_t GetSampleSize(); + + // Device volume & route handling APIs + + // Audio stream callbacks + + /** + * Register for callbacks associated with the playback stream created using CreateStream + * + * @param cb indicates pointer for registered callbacks + * @return none + */ + void RegisterAudioRendererCallbacks(const AudioRendererCallbacks &cb); + + /** + * Register for callbacks associated with the record stream created using CreateStream + * + * @param cb indicates pointer for registered callbacks + * @return none + */ + void RegisterAudioRecorderCallbacks(const AudioRecorderCallbacks &cb); + +private: + pa_threaded_mainloop *mainLoop; + pa_mainloop_api *api; + pa_context *context; + pa_stream *paStream; + pa_sample_spec sampleSpec; + + std::mutex mtx; + + AudioCache acache; + const void* internalReadBuffer; + size_t internalRdBufLen; + size_t internalRdBufIndex; + int32_t streamCmdStatus; + bool isMainLoopStarted; + bool isContextConnected; + bool isStreamConnected; + + // To be set while using audio stream + // functionality for callbacks + AudioRendererCallbacks* mAudioRendererCallbacks; + AudioRecorderCallbacks* mAudioRecorderCallbacks; + + std::map sinkDevices; + std::map sourceDevices; + std::map sinkInputs; + std::map sourceOutputs; + std::map clientInfo; + + ASClientType eAudioClientType; + + uint32_t underFlowCount; + int32_t ConnectStreamToPA(); + + // Audio cache related functions. These APIs are applicable only for playback scenarios + int32_t InitializeAudioCache(); + size_t WriteToAudioCache(const StreamBuffer &stream); + int32_t DrainAudioCache(); + + int32_t UpdateReadBuffer(uint8_t *buffer, size_t &length, size_t &readSize); + int32_t PaWriteStream(const uint8_t *buffer, size_t &length); + + // Error code used + static const uint32_t AUDIO_CLIENT_SUCCESS = 0; + static const uint32_t AUDIO_CLIENT_ERR = -1; + static const uint32_t AUDIO_CLIENT_INVALID_PARAMS_ERR = -2; + static const uint32_t AUDIO_CLIENT_INIT_ERR = -3; + static const uint32_t AUDIO_CLIENT_CREATE_STREAM_ERR = -4; + static const uint32_t AUDIO_CLIENT_START_STREAM_ERR = -5; + static const uint32_t AUDIO_CLIENT_READ_STREAM_ERR = -6; + static const uint32_t AUDIO_CLIENT_WRITE_STREAM_ERR = -7; + static const uint32_t AUDIO_CLIENT_PA_ERR = -8; + + // Default values + static const uint32_t MINIMUM_BUFFER_SIZE = 1024; + static const uint32_t DEFAULT_SAMPLING_RATE = 44100; + static const uint8_t DEFAULT_CHANNEL_COUNT = 2; + static const uint8_t DEFAULT_SAMPLE_SIZE = 2; + static const uint32_t DEFAULT_STREAM_VOLUME = 65536; + static const std::string GetStreamName(AudioStreamType audioType); + static pa_sample_spec ConvertToPAAudioParams(AudioStreamParams audioParams); + static AudioStreamParams ConvertFromPAAudioParams(pa_sample_spec paSampleSpec); + + // Resets PA audio client and free up resources if any with this API + void ResetPAAudioClient(); + + // Callbacks to be implemented + static void PAStreamStateCb(pa_stream *stream, void *userdata); + static void PAStreamUnderFlowCb(pa_stream *stream, void *userdata); + static void PAContextStateCb(pa_context *context, void *userdata); + static void PAStreamRequestCb(pa_stream *stream, size_t length, void *userdata); + static void PAStreamCmdSuccessCb(pa_stream *stream, int32_t success, void *userdata); + static void PAStreamLatencyUpdateCb(pa_stream *stream, void *userdata); +}; +} // namespace AudioStandard +} // namespace OHOS +#endif // AUDIO_SERVICE_CLIENT_H diff --git a/services/include/server/audio_server.h b/services/include/server/audio_server.h old mode 100755 new mode 100644 index 2e9de4ec2e9b5fe3eb09c7af61072955b532d85b..2909574a0749e29b2b408ce3a40f050fd8daeb12 --- a/services/include/server/audio_server.h +++ b/services/include/server/audio_server.h @@ -18,14 +18,14 @@ #include #include - +#include #include "iremote_stub.h" #include "system_ability.h" -#include "audio_svc_manager.h" +#include "audio_system_manager.h" #include "audio_manager_base.h" -#include "audio_device_descriptor.h" namespace OHOS { +namespace AudioStandard { class AudioServer : public SystemAbility, public AudioManagerStub { DECLARE_SYSTEM_ABILITY(AudioServer); public: @@ -35,16 +35,22 @@ public: void OnDump() override; void OnStart() override; void OnStop() override; - void SetVolume(AudioSvcManager::AudioVolumeType volumeType, int32_t volume) override; - int32_t GetVolume(AudioSvcManager::AudioVolumeType volumeType) override; - int32_t GetMaxVolume(AudioSvcManager::AudioVolumeType volumeType) override; - int32_t GetMinVolume(AudioSvcManager::AudioVolumeType volumeType) override; + float GetMaxVolume(AudioSystemManager::AudioVolumeType volumeType) override; + float GetMinVolume(AudioSystemManager::AudioVolumeType volumeType) override; + int32_t SetMicrophoneMute(bool isMute) override; + bool IsMicrophoneMute() override; std::vector> GetDevices(AudioDeviceDescriptor::DeviceFlag deviceFlag) override; + static void* paDaemonThread(void* arg); + void SetAudioParameter(const std::string key, const std::string value) override; + const std::string GetAudioParameter(const std::string key) override; private: - static const int32_t MAX_VOLUME = 15; - static const int32_t MIN_VOLUME = 0; - static std::unordered_map AudioStreamVolumeMap; + static constexpr float MAX_VOLUME = 1.0; + static constexpr float MIN_VOLUME = 0; + static std::unordered_map AudioStreamVolumeMap; std::vector> audioDeviceDescriptor_; + static std::map audioParameters; + pthread_t m_paDaemonThread; }; +} // namespace AudioStandard } // namespace OHOS #endif // ST_AUDIO_SERVER_H diff --git a/services/src/client/audio_svc_manager.cpp b/services/src/audio_policy/client/audio_policy_manager.cpp old mode 100755 new mode 100644 similarity index 34% rename from services/src/client/audio_svc_manager.cpp rename to services/src/audio_policy/client/audio_policy_manager.cpp index e03c9f1e6c46daad69739529a08a5613db177b6c..aafbd0b7bf2bb99d5bd3572607b71130bfae4359 --- a/services/src/client/audio_svc_manager.cpp +++ b/services/src/audio_policy/client/audio_policy_manager.cpp @@ -13,80 +13,79 @@ * limitations under the License. */ +#include "audio_policy_manager.h" +#include "audio_policy_proxy.h" #include "iservice_registry.h" -#include "audio_manager_proxy.h" -#include "audio_svc_manager.h" #include "media_log.h" #include "system_ability_definition.h" namespace OHOS { -static sptr g_sProxy = nullptr; +namespace AudioStandard { +static sptr g_sProxy = nullptr; +void AudioPolicyManager::Init() +{ + auto samgr = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager(); + if (samgr == nullptr) { + MEDIA_ERR_LOG("AudioPolicyManager::init failed"); + return; + } + + sptr object = samgr->GetSystemAbility(AUDIO_POLICY_SERVICE_ID); + if (object == nullptr) { + MEDIA_DEBUG_LOG("AudioPolicyManager::object is NULL."); + } + + g_sProxy = iface_cast(object); + if (g_sProxy == nullptr) { + MEDIA_DEBUG_LOG("AudioPolicyManager::init g_sProxy is NULL."); + } else { + MEDIA_DEBUG_LOG("AudioPolicyManager::init g_sProxy is assigned."); + } +} -AudioSvcManager::AudioSvcManager() +int32_t AudioPolicyManager::SetStreamVolume(AudioStreamType streamType, float volume) { - MEDIA_DEBUG_LOG("AudioSvcManager start"); - init(); + return g_sProxy->SetStreamVolume(streamType, volume); } -AudioSvcManager::~AudioSvcManager() +int32_t AudioPolicyManager::SetRingerMode(AudioRingerMode ringMode) { - MEDIA_DEBUG_LOG("AudioSvcManager::~AudioSvcManager"); + return g_sProxy->SetRingerMode(ringMode); } -AudioSvcManager* AudioSvcManager::GetInstance() +AudioRingerMode AudioPolicyManager::GetRingerMode() { - static AudioSvcManager audioManager; - return &audioManager; + return g_sProxy->GetRingerMode(); } -void AudioSvcManager::init() +float AudioPolicyManager::GetStreamVolume(AudioStreamType streamType) { - MEDIA_DEBUG_LOG("AudioSvcManager::init start"); - auto samgr = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager(); - if (samgr == nullptr) { - MEDIA_ERR_LOG("AudioSvcManager::init failed"); - return; - } - sptr object = samgr->GetSystemAbility(AUDIO_DISTRIBUTED_SERVICE_ID); - if (object == nullptr) { - MEDIA_DEBUG_LOG("AudioSvcManager::object is NULL."); - } - g_sProxy = iface_cast(object); - if (g_sProxy == nullptr) { - MEDIA_DEBUG_LOG("AudioSvcManager::init g_sProxy is NULL."); - } else { - MEDIA_DEBUG_LOG("AudioSvcManager::init g_sProxy is assigned."); - } - MEDIA_ERR_LOG("AudioSvcManager::init end"); + return g_sProxy->GetStreamVolume(streamType); } -void AudioSvcManager::SetVolume(AudioSvcManager::AudioVolumeType volumeType, int32_t volume) +int32_t AudioPolicyManager::SetStreamMute(AudioStreamType streamType, bool mute) { - MEDIA_DEBUG_LOG("AudioSvcManager::SetVolume Client"); - g_sProxy->SetVolume(volumeType, volume); + return g_sProxy->SetStreamMute(streamType, mute); } -int AudioSvcManager::GetVolume(AudioSvcManager::AudioVolumeType volumeType) +bool AudioPolicyManager::GetStreamMute(AudioStreamType streamType) { - MEDIA_DEBUG_LOG("AudioSvcManager::GetVolume Client"); - return g_sProxy->GetVolume(volumeType); + return g_sProxy->GetStreamMute(streamType); } -int AudioSvcManager::GetMaxVolume(AudioSvcManager::AudioVolumeType volumeType) +bool AudioPolicyManager::IsStreamActive(AudioStreamType streamType) { - MEDIA_DEBUG_LOG("AudioSvcManager::GetMaxVolume Client"); - return g_sProxy->GetMaxVolume(volumeType); + return g_sProxy->IsStreamActive(streamType); } -int AudioSvcManager::GetMinVolume(AudioSvcManager::AudioVolumeType volumeType) +int32_t AudioPolicyManager::SetDeviceActive(DeviceType deviceType, bool active) { - MEDIA_DEBUG_LOG("AudioSvcManager::GetMinVolume Client"); - return g_sProxy->GetMinVolume(volumeType); + return g_sProxy->SetDeviceActive(deviceType, active); } -std::vector> AudioSvcManager::GetDevices(AudioDeviceDescriptor::DeviceFlag deviceFlag) +bool AudioPolicyManager::IsDeviceActive(DeviceType deviceType) { - MEDIA_DEBUG_LOG("AudioSvcManager::GetDevices Client"); - return g_sProxy->GetDevices(deviceFlag); + return g_sProxy->IsDeviceActive(deviceType); } +} // namespace AudioStandard } // namespace OHOS diff --git a/services/src/audio_policy/client/audio_policy_proxy.cpp b/services/src/audio_policy/client/audio_policy_proxy.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b5b59b099bde71735934f294371e48b4246e8a87 --- /dev/null +++ b/services/src/audio_policy/client/audio_policy_proxy.cpp @@ -0,0 +1,164 @@ +/* + * Copyright (C) 2021 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_policy_manager.h" +#include "audio_policy_proxy.h" +#include "media_log.h" + +namespace OHOS { +namespace AudioStandard { +AudioPolicyProxy::AudioPolicyProxy(const sptr &impl) + : IRemoteProxy(impl) +{ +} + +int32_t AudioPolicyProxy::SetStreamVolume(AudioStreamType streamType, float volume) +{ + MessageParcel data; + MessageParcel reply; + MessageOption option; + data.WriteInt32(static_cast(streamType)); + data.WriteFloat(volume); + + int32_t error = Remote()->SendRequest(SET_STREAM_VOLUME, data, reply, option); + if (error != ERR_NONE) { + MEDIA_ERR_LOG("set volume failed, error: %d", error); + return error; + } + return reply.ReadInt32(); +} + +int32_t AudioPolicyProxy::SetRingerMode(AudioRingerMode ringMode) +{ + MessageParcel data; + MessageParcel reply; + MessageOption option; + data.WriteInt32(static_cast(ringMode)); + + int32_t error = Remote()->SendRequest(SET_RINGER_MODE, data, reply, option); + if (error != ERR_NONE) { + MEDIA_ERR_LOG("set ringermode failed, error: %d", error); + return error; + } + + return reply.ReadInt32(); +} + +AudioRingerMode AudioPolicyProxy::GetRingerMode() +{ + MessageParcel data; + MessageParcel reply; + MessageOption option; + + int32_t error = Remote()->SendRequest(GET_RINGER_MODE, data, reply, option); + if (error != ERR_NONE) { + MEDIA_ERR_LOG("get ringermode failed, error: %d", error); + } + return static_cast(reply.ReadInt32()); +} + +float AudioPolicyProxy::GetStreamVolume(AudioStreamType streamType) +{ + MessageParcel data; + MessageParcel reply; + MessageOption option; + data.WriteInt32(static_cast(streamType)); + + int32_t error = Remote()->SendRequest(GET_STREAM_VOLUME, data, reply, option); + if (error != ERR_NONE) { + MEDIA_ERR_LOG("get volume failed, error: %d", error); + return error; + } + return reply.ReadFloat(); +} + +int32_t AudioPolicyProxy::SetStreamMute(AudioStreamType streamType, bool mute) +{ + MessageParcel data; + MessageParcel reply; + MessageOption option; + data.WriteInt32(static_cast(streamType)); + data.WriteBool(mute); + + int32_t error = Remote()->SendRequest(SET_STREAM_MUTE, data, reply, option); + if (error != ERR_NONE) { + MEDIA_ERR_LOG("set mute failed, error: %d", error); + return error; + } + return reply.ReadInt32(); +} + +bool AudioPolicyProxy::GetStreamMute(AudioStreamType streamType) +{ + MessageParcel data; + MessageParcel reply; + MessageOption option; + data.WriteInt32(static_cast(streamType)); + + int32_t error = Remote()->SendRequest(GET_STREAM_MUTE, data, reply, option); + if (error != ERR_NONE) { + MEDIA_ERR_LOG("get mute failed, error: %d", error); + return error; + } + return reply.ReadBool(); +} + +bool AudioPolicyProxy::IsStreamActive(AudioStreamType streamType) +{ + MessageParcel data; + MessageParcel reply; + MessageOption option; + data.WriteInt32(static_cast(streamType)); + + int32_t error = Remote()->SendRequest(IS_STREAM_ACTIVE, data, reply, option); + if (error != ERR_NONE) { + MEDIA_ERR_LOG("isStreamActive failed, error: %d", error); + return false; + } + return reply.ReadBool(); +} + +int32_t AudioPolicyProxy::SetDeviceActive(DeviceType deviceType, bool active) +{ + MessageParcel data; + MessageParcel reply; + MessageOption option; + data.WriteInt32(static_cast(deviceType)); + data.WriteBool(active); + + int32_t error = Remote()->SendRequest(SET_DEVICE_ACTIVE, data, reply, option); + if (error != ERR_NONE) { + MEDIA_ERR_LOG("set device active failed, error: %d", error); + return error; + } + return reply.ReadInt32(); +} + +bool AudioPolicyProxy::IsDeviceActive(DeviceType deviceType) +{ + MessageParcel data; + MessageParcel reply; + MessageOption option; + data.WriteInt32(static_cast(deviceType)); + + int32_t error = Remote()->SendRequest(IS_DEVICE_ACTIVE, data, reply, option); + if (error != ERR_NONE) { + MEDIA_ERR_LOG("is device active failed, error: %d", error); + return false; + } + return reply.ReadBool(); +} +} // namespace AudioStandard +} // namespace OHOS diff --git a/services/src/audio_policy/server/audio_policy_manager_stub.cpp b/services/src/audio_policy/server/audio_policy_manager_stub.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2594769d58e91454ddba657bda5413e6c8830a7f --- /dev/null +++ b/services/src/audio_policy/server/audio_policy_manager_stub.cpp @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2021 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_errors.h" +#include "audio_policy_manager_stub.h" +#include "audio_policy_server.h" +#include "audio_policy_types.h" +#include "media_log.h" + +namespace OHOS { +namespace AudioStandard { +void AudioPolicyManagerStub::SetStreamVolumeInternal(MessageParcel &data, MessageParcel &reply) +{ + AudioStreamType streamType = static_cast(data.ReadInt32()); + float volume = data.ReadFloat(); + int result = SetStreamVolume(streamType, volume); + if (result == SUCCESS) + reply.WriteInt32(MEDIA_OK); + else + reply.WriteInt32(MEDIA_ERR); +} + +void AudioPolicyManagerStub::SetRingerModeInternal(MessageParcel &data, MessageParcel &reply) +{ + AudioRingerMode rMode = static_cast(data.ReadInt32()); + int32_t result = SetRingerMode(rMode); + reply.WriteInt32(result); +} + +void AudioPolicyManagerStub::GetRingerModeInternal(MessageParcel &reply) +{ + AudioRingerMode rMode = GetRingerMode(); + reply.WriteInt32(static_cast(rMode)); +} + +void AudioPolicyManagerStub::GetStreamVolumeInternal(MessageParcel &data, MessageParcel &reply) +{ + AudioStreamType streamType = static_cast(data.ReadInt32()); + float volume = GetStreamVolume(streamType); + reply.WriteFloat(volume); +} + +void AudioPolicyManagerStub::SetStreamMuteInternal(MessageParcel &data, MessageParcel &reply) +{ + AudioStreamType streamType = static_cast(data.ReadInt32()); + bool mute = data.ReadBool(); + int result = SetStreamMute(streamType, mute); + if (result == SUCCESS) + reply.WriteInt32(MEDIA_OK); + else + reply.WriteInt32(MEDIA_ERR); +} + +void AudioPolicyManagerStub::GetStreamMuteInternal(MessageParcel &data, MessageParcel &reply) +{ + AudioStreamType streamType = static_cast(data.ReadInt32()); + bool mute = GetStreamMute(streamType); + reply.WriteBool(mute); +} + +void AudioPolicyManagerStub::IsStreamActiveInternal(MessageParcel &data, MessageParcel &reply) +{ + AudioStreamType streamType = static_cast(data.ReadInt32()); + bool isActive = IsStreamActive(streamType); + reply.WriteBool(isActive); +} + +void AudioPolicyManagerStub::SetDeviceActiveInternal(MessageParcel &data, MessageParcel &reply) +{ + DeviceType deviceType = static_cast(data.ReadInt32()); + bool active = data.ReadBool(); + int32_t result = SetDeviceActive(deviceType, active); + if (result == SUCCESS) + reply.WriteInt32(MEDIA_OK); + else + reply.WriteInt32(MEDIA_ERR); +} + +void AudioPolicyManagerStub::IsDeviceActiveInternal(MessageParcel &data, MessageParcel &reply) +{ + DeviceType deviceType = static_cast(data.ReadInt32()); + bool result = IsDeviceActive(deviceType); + reply.WriteBool(result); +} + +int AudioPolicyManagerStub::OnRemoteRequest( + uint32_t code, MessageParcel &data, MessageParcel &reply, MessageOption &option) +{ + switch (code) { + case SET_STREAM_VOLUME: + SetStreamVolumeInternal(data, reply); + break; + + case SET_RINGER_MODE: + SetRingerModeInternal(data, reply); + break; + + case GET_RINGER_MODE: + GetRingerModeInternal(reply); + break; + + case GET_STREAM_VOLUME: + GetStreamVolumeInternal(data, reply); + break; + + case SET_STREAM_MUTE: + SetStreamMuteInternal(data, reply); + break; + + case GET_STREAM_MUTE: + GetStreamMuteInternal(data, reply); + break; + + case IS_STREAM_ACTIVE: + IsStreamActiveInternal(data, reply); + break; + + case SET_DEVICE_ACTIVE: + SetDeviceActiveInternal(data, reply); + break; + + case IS_DEVICE_ACTIVE: + IsDeviceActiveInternal(data, reply); + break; + + default: + MEDIA_ERR_LOG("default case, need check AudioPolicyManagerStub"); + return IPCObjectStub::OnRemoteRequest(code, data, reply, option); + } + return MEDIA_OK; +} + +bool AudioPolicyManagerStub::IsPermissionValid() +{ + return true; +} +} // namespace audio_policy +} // namespace OHOS diff --git a/services/src/audio_policy/server/audio_policy_server.cpp b/services/src/audio_policy/server/audio_policy_server.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0cfcdb581608cf028608909eed5ae63aa33f9f16 --- /dev/null +++ b/services/src/audio_policy/server/audio_policy_server.cpp @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2021 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 "audio_policy_server.h" +#include "iservice_registry.h" +#include "media_log.h" +#include "system_ability_definition.h" + +namespace OHOS { +namespace AudioStandard { +REGISTER_SYSTEM_ABILITY_BY_ID(AudioPolicyServer, AUDIO_POLICY_SERVICE_ID, true) + +AudioPolicyServer::AudioPolicyServer(int32_t systemAbilityId, bool runOnCreate) + : SystemAbility(systemAbilityId, runOnCreate), + mPolicyService(AudioPolicyService::GetAudioPolicyService()) +{ +} + +void AudioPolicyServer::OnDump() +{ + return; +} + +void AudioPolicyServer::OnStart() +{ + bool res = Publish(this); + if (res) { + MEDIA_DEBUG_LOG("AudioPolicyService OnStart res=%d", res); + } + + mPolicyService.Init(); + return; +} + +void AudioPolicyServer::OnStop() +{ + mPolicyService.Deinit(); + return; +} + +int32_t AudioPolicyServer::SetStreamVolume(AudioStreamType streamType, float volume) +{ + return mPolicyService.SetStreamVolume(streamType, volume); +} + +float AudioPolicyServer::GetStreamVolume(AudioStreamType streamType) +{ + return mPolicyService.GetStreamVolume(streamType); +} + +int32_t AudioPolicyServer::SetStreamMute(AudioStreamType streamType, bool mute) +{ + return mPolicyService.SetStreamMute(streamType, mute); +} + +bool AudioPolicyServer::GetStreamMute(AudioStreamType streamType) +{ + return mPolicyService.GetStreamMute(streamType); +} + +bool AudioPolicyServer::IsStreamActive(AudioStreamType streamType) +{ + return mPolicyService.IsStreamActive(streamType); +} + +int32_t AudioPolicyServer::SetDeviceActive(DeviceType deviceType, bool active) +{ + return mPolicyService.SetDeviceActive(deviceType, active); +} + +bool AudioPolicyServer::IsDeviceActive(DeviceType deviceType) +{ + return mPolicyService.IsDeviceActive(deviceType); +} + +int32_t AudioPolicyServer::SetRingerMode(AudioRingerMode ringMode) +{ + return mPolicyService.SetRingerMode(ringMode); +} + +AudioRingerMode AudioPolicyServer::GetRingerMode() +{ + return mPolicyService.GetRingerMode(); +} +} // namespace AudioStandard +} // namespace OHOS diff --git a/services/src/audio_policy/server/etc/audio_policy_config.xml b/services/src/audio_policy/server/etc/audio_policy_config.xml new file mode 100644 index 0000000000000000000000000000000000000000..20a1794dbc5198c2657aff4247845b49c6ef101a --- /dev/null +++ b/services/src/audio_policy/server/etc/audio_policy_config.xml @@ -0,0 +1,35 @@ + + + + + + + + Speaker + Built-In Mic + + + + + + + + + Speaker + Built-In Mic + + + + + + + + + + + + + + + + diff --git a/services/src/audio_policy/server/service/include/audio_policy_service.h b/services/src/audio_policy/server/service/include/audio_policy_service.h new file mode 100644 index 0000000000000000000000000000000000000000..124c0dbe356a71dd8aa179398a58ffe42385990b --- /dev/null +++ b/services/src/audio_policy/server/service/include/audio_policy_service.h @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2021 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 ST_AUDIO_POLICY_SERVICE_H +#define ST_AUDIO_POLICY_SERVICE_H + +#include "audio_info.h" +#include "audio_policy_manager_factory.h" +#include "iaudio_policy_interface.h" +#include "iport_observer.h" +#include "parser_factory.h" + +#include +#include +#include + +namespace OHOS { +namespace AudioStandard { +class AudioPolicyService : public IPortObserver { +public: + static constexpr char HDI_SINK[] = "hdi_output"; + static constexpr char HDI_SOURCE[] = "hdi_input"; + static constexpr char BLUEZ_SINK[] = "fifo_output"; + static constexpr char BLUEZ_SOURCE[] = "fifo_input"; + + static AudioPolicyService& GetAudioPolicyService() + { + static AudioPolicyService audioPolicyService; + return audioPolicyService; + } + + bool Init(void); + void Deinit(void); + + int32_t SetStreamVolume(AudioStreamType streamType, float volume) const; + + float GetStreamVolume(AudioStreamType streamType) const; + + int32_t SetStreamMute(AudioStreamType streamType, bool mute) const; + + bool GetStreamMute(AudioStreamType streamType) const; + + bool IsStreamActive(AudioStreamType streamType) const; + + int32_t SetDeviceActive(DeviceType deviceType, bool active); + + bool IsDeviceActive(DeviceType deviceType) const; + + int32_t SetRingerMode(AudioRingerMode ringMode); + + AudioRingerMode GetRingerMode() const; + + // Parser callbacks + void OnAudioPortAvailable(std::shared_ptr portInfo); + + void OnAudioPortPinAvailable(std::shared_ptr portInfo); + + void OnDefaultOutputPortPin(DeviceType device); + + void OnDefaultInputPortPin(DeviceType device); + +private: + + AudioPolicyService() + : mAudioPolicyManager(AudioPolicyManagerFactory::GetAudioPolicyManager()), + mConfigParser(ParserFactory::GetInstance().CreateParser(*this)) + { + } + + virtual ~AudioPolicyService() {} + + AudioIOHandle GetAudioIOHandle(DeviceType deviceType); + std::list& GetActiveDevicesList(DeviceType deviceType) + { + switch (deviceType) { + case SPEAKER: + case BLUETOOTH_A2DP: + return mActiveOutputDevices; + case MIC: + case BLUETOOTH_SCO: + return mActiveInputDevices; + default: + return mActiveOutputDevices; // Default case return Output device + } + } + + IAudioPolicyInterface& mAudioPolicyManager; + Parser& mConfigParser; + std::unordered_map mIOHandles; + std::list mActiveOutputDevices; + std::list mActiveInputDevices; +}; +} // namespace AudioStandard +} // namespace OHOS + +#endif // ST_AUDIO_POLICY_SERVICE_H diff --git a/services/src/audio_policy/server/service/include/common/audio_config.h b/services/src/audio_policy/server/service/include/common/audio_config.h new file mode 100644 index 0000000000000000000000000000000000000000..91524a270b0be21e3147b3efe7cbd0ff4f505a3e --- /dev/null +++ b/services/src/audio_policy/server/service/include/common/audio_config.h @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2021 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 ST_AUDIO_CONFIG_H +#define ST_AUDIO_CONFIG_H + +#include + +#include "audio_info.h" + +#ifdef __cplusplus +extern "C" { +#endif +#include +#include +#ifdef __cplusplus +} +#endif + +namespace OHOS { +namespace AudioStandard { +enum NodeName { + MODULES, + MODULE, + BUILT_IN_DEVICES, + DEFAULT_OUTPUT_DEVICE, + DEFAULT_INPUT_DEVICE, + AUDIO_PORTS, + AUDIO_PORT, + AUDIO_PORT_PINS, + AUDIO_PORT_PIN, + UNKNOWN +}; + +enum PortType { + TYPE_AUDIO_PORT, + TYPE_AUDIO_PORT_PIN, +}; + +class PortInfo { +public: + PortType type; + char* name; + char* role; + char* rate; + char* channels; + char* buffer_size; + char* fileName; + + PortInfo() + : name (nullptr), + role (nullptr), + rate (nullptr), + channels (nullptr), + buffer_size (nullptr), + fileName (nullptr) + { + } + + ~PortInfo() + { + if (name != nullptr) + xmlFree(reinterpret_cast(name)); + if (role != nullptr) + xmlFree(reinterpret_cast(role)); + if (rate != nullptr) + xmlFree(reinterpret_cast(rate)); + if (channels != nullptr) + xmlFree(reinterpret_cast(channels)); + if (buffer_size != nullptr) + xmlFree(reinterpret_cast(buffer_size)); + if (fileName != nullptr) + xmlFree(reinterpret_cast(fileName)); + } +}; + +class AudioPortInfo : public PortInfo { +public: + AudioPortInfo() {} + + virtual ~AudioPortInfo() {} +}; + +struct AudioPortPinInfo : public PortInfo { +public: + char* pinType; + AudioPortPinInfo() + : pinType(nullptr) + { + } + + virtual ~AudioPortPinInfo() {} +}; +} // namespace AudioStandard +} // namespace OHOS + +#endif // ST_AUDIO_CONFIG_H diff --git a/services/src/audio_policy/server/service/include/config/parser.h b/services/src/audio_policy/server/service/include/config/parser.h new file mode 100644 index 0000000000000000000000000000000000000000..5d0140f37c03af439380378bcbf21d587fac527b --- /dev/null +++ b/services/src/audio_policy/server/service/include/config/parser.h @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2021 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 ST_PARSER_H +#define ST_PARSER_H + +namespace OHOS { +namespace AudioStandard { +class Parser { +public: + virtual ~Parser() {} + virtual bool LoadConfiguration() = 0; + virtual bool Parse() = 0; + virtual void Destroy() = 0; +}; +} // namespace AudioStandard +} // namespace OHOS + +#endif // ST_PARSER_H diff --git a/services/src/audio_policy/server/service/include/config/parser_factory.h b/services/src/audio_policy/server/service/include/config/parser_factory.h new file mode 100644 index 0000000000000000000000000000000000000000..038759aad4d64389f99aaaa42d9b75cd826ae134 --- /dev/null +++ b/services/src/audio_policy/server/service/include/config/parser_factory.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2021 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 ST_PARSER_FACTORY_H +#define ST_PARSER_FACTORY_H + +#include "xml_parser.h" + +namespace OHOS { +namespace AudioStandard { +class ParserFactory { +public: + static ParserFactory& GetInstance() + { + static ParserFactory instance; + return instance; + } + + Parser& CreateParser(IPortObserver& observer) + { + static XMLParser parser(observer); + return parser; + } +private: + ParserFactory(ParserFactory&) = delete; + ParserFactory() {} +}; +} // namespace AudioStandard +} // namespace OHOS + +#endif // ST_PARSER_FACTORY_H diff --git a/services/src/audio_policy/server/service/include/config/xml_parser.h b/services/src/audio_policy/server/service/include/config/xml_parser.h new file mode 100644 index 0000000000000000000000000000000000000000..b9a117f4c64ed53067564ae087511f472b7efceb --- /dev/null +++ b/services/src/audio_policy/server/service/include/config/xml_parser.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2021 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 ST_XML_PARSER_H +#define ST_XML_PARSER_H + +#ifdef __cplusplus +extern "C" { +#endif +#include +#include +#ifdef __cplusplus +} +#endif + +#include "audio_config.h" +#include "iport_observer.h" +#include "parser.h" + +namespace OHOS { +namespace AudioStandard { +class XMLParser : public Parser { +public: + static constexpr char CONFIG_FILE[] = "/etc/audio/audio_policy_config.xml"; + + bool LoadConfiguration() final; + bool Parse() final; + void Destroy() final; + + explicit XMLParser(IPortObserver& observer) + : mPortObserver(observer), + mDoc(nullptr) + { + } + + virtual ~XMLParser() + { + Destroy(); + } +private: + bool ParseInternal(xmlNode* node); + NodeName GetNodeNameAsInt(xmlNode* node); + void ParseBuiltInDevices(xmlNode* node); + void ParseDefaultOutputDevice(xmlNode* node); + void ParseDefaultInputDevice(xmlNode* node); + void ParseAudioPorts(xmlNode* node); + void ParseAudioPortPins(xmlNode* node); + DeviceType GetDeviceType(xmlChar* device); + + IPortObserver& mPortObserver; + xmlDoc* mDoc; +}; +} // namespace AudioStandard +} // namespace OHOS + +#endif // ST_XML_PARSER_H diff --git a/services/src/audio_policy/server/service/include/interface/iaudio_policy_interface.h b/services/src/audio_policy/server/service/include/interface/iaudio_policy_interface.h new file mode 100644 index 0000000000000000000000000000000000000000..b026d2e097838aab2bfaba6c594a71d04ff2957a --- /dev/null +++ b/services/src/audio_policy/server/service/include/interface/iaudio_policy_interface.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2021 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 ST_AUDIO_POLICY_INTERFACE_H +#define ST_AUDIO_POLICY_INTERFACE_H + +#include "audio_config.h" +#include "audio_info.h" +#include "audio_policy_types.h" + +#include +#include + +namespace OHOS { +namespace AudioStandard { +class IAudioPolicyInterface { +public: + virtual ~IAudioPolicyInterface() {} + + virtual bool Init() = 0; + + virtual int32_t SetStreamVolume(AudioStreamType streamType, float volume) = 0; + + virtual float GetStreamVolume(AudioStreamType streamType) = 0; + + virtual int32_t SetStreamMute(AudioStreamType streamType, bool mute) = 0; + + virtual bool GetStreamMute(AudioStreamType streamType) = 0; + + virtual bool IsStreamActive(AudioStreamType streamType) = 0; + + virtual AudioIOHandle OpenAudioPort(std::shared_ptr audioPortInfo) = 0; + + virtual int32_t CloseAudioPort(AudioIOHandle ioHandle) = 0; + + virtual int32_t SetDeviceActive(AudioIOHandle ioHandle, DeviceType deviceType, std::string name, bool active) = 0; + + virtual int32_t SetRingerMode(AudioRingerMode ringerMode) = 0; + + virtual AudioRingerMode GetRingerMode() = 0; +}; +} // namespace AudioStandard +} // namespace OHOS + +#endif // ST_AUDIO_POLICY_INTERFACE_H diff --git a/services/src/audio_policy/server/service/include/interface/iport_observer.h b/services/src/audio_policy/server/service/include/interface/iport_observer.h new file mode 100644 index 0000000000000000000000000000000000000000..af7ef9ab68cde912018ae414d648cf94d5b9aeb9 --- /dev/null +++ b/services/src/audio_policy/server/service/include/interface/iport_observer.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2021 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 ST_AUDIO_PORT_OBSERVER_H +#define ST_AUDIO_PORT_OBSERVER_H + +#include "audio_info.h" +#include "iaudio_policy_interface.h" + +namespace OHOS { +namespace AudioStandard { +class IPortObserver { +public: + virtual void OnAudioPortAvailable(std::shared_ptr portInfo) = 0; + virtual void OnAudioPortPinAvailable(std::shared_ptr portInfo) = 0; + virtual void OnDefaultOutputPortPin(DeviceType device) = 0; + virtual void OnDefaultInputPortPin(DeviceType device) = 0; +}; +} // namespace AudioStandard +} // namespace OHOS +#endif diff --git a/services/src/audio_policy/server/service/include/manager/audio_policy_manager_factory.h b/services/src/audio_policy/server/service/include/manager/audio_policy_manager_factory.h new file mode 100644 index 0000000000000000000000000000000000000000..f9867aaa106cd43ed53974a276f54d825ab38ba7 --- /dev/null +++ b/services/src/audio_policy/server/service/include/manager/audio_policy_manager_factory.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2021 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 ST_AUDIO_POLICY_MANAGER_FACTORY_H +#define ST_AUDIO_POLICY_MANAGER_FACTORY_H + +#include +#include "pulseaudio_policy_manager.h" + +namespace OHOS { +namespace AudioStandard { +class AudioPolicyManagerFactory { +public: + static IAudioPolicyInterface& GetAudioPolicyManager(void) + { + return PulseAudioPolicyManager::GetInstance(); + } +}; +} // namespace AudioStandard +} // namespace OHOS + +#endif // ST_AUDIO_POLICY_MANAGER_FACTORY_H diff --git a/services/src/audio_policy/server/service/include/manager/pulseaudio_policy_manager.h b/services/src/audio_policy/server/service/include/manager/pulseaudio_policy_manager.h new file mode 100644 index 0000000000000000000000000000000000000000..dc8eca5bcb42e07be0f1e2bc04070f136c88b71d --- /dev/null +++ b/services/src/audio_policy/server/service/include/manager/pulseaudio_policy_manager.h @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2021 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 ST_PULSEAUDIO_POLICY_MANAGER_H +#define ST_PULSEAUDIO_POLICY_MANAGER_H + +#ifdef __cplusplus +extern "C" { +#endif +#include +#ifdef __cplusplus +} +#endif + +#include +#include + +#include "distributed_kv_data_manager.h" +#include "iaudio_policy_interface.h" +#include "types.h" + +namespace OHOS { +namespace AudioStandard { +using namespace OHOS::DistributedKv; + +class PulseAudioPolicyManager : public IAudioPolicyInterface { +public: + static constexpr char HDI_SINK[] = "libmodule-hdi-sink.z.so"; + static constexpr char HDI_SOURCE[] = "libmodule-hdi-source.z.so"; + static constexpr char PIPE_SINK[] = "libmodule-pipe-sink.z.so"; + static constexpr char PIPE_SOURCE[] = "libmodule-pipe-source.z.so"; + static constexpr float MAX_VOLUME = 1.0f; + static constexpr float MIN_VOLUME = 0.0f; + static constexpr uint32_t PA_CONNECT_RETRY_SLEEP_IN_MICRO_SECONDS = 500000; + + bool Init(); + void Deinit(void); + + std::string GetPolicyManagerName(); + + static IAudioPolicyInterface& GetInstance() + { + static PulseAudioPolicyManager policyManager; + return policyManager; + } + + int32_t SetStreamVolume(AudioStreamType streamType, float volume); + + float GetStreamVolume(AudioStreamType streamType); + + int32_t SetStreamMute(AudioStreamType streamType, bool mute); + + bool GetStreamMute(AudioStreamType streamType); + + bool IsStreamActive(AudioStreamType streamType); + + AudioIOHandle OpenAudioPort(std::shared_ptr audioPortInfo); + + int32_t CloseAudioPort(AudioIOHandle ioHandle); + + int32_t SetDeviceActive(AudioIOHandle ioHandle, DeviceType deviceType, std::string name, bool active); + + int32_t SetRingerMode(AudioRingerMode ringerMode); + + AudioRingerMode GetRingerMode(void); + + // Static Member functions + static void ContextStateCb(pa_context *c, void *userdata); + + static void SubscribeCb(pa_context *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata); + + static void ModuleLoadCb(pa_context *c, uint32_t idx, void *userdata); + + static void GetSinkInputInfoVolumeCb(pa_context *c, const pa_sink_input_info *i, int eol, void *userdata); + + static void GetSinkInputInfoCb(pa_context *c, const pa_sink_input_info *i, int eol, void *userdata); + + static void GetSinkInputInfoMuteCb(pa_context *c, const pa_sink_input_info *i, int eol, void *userdata); + + static void GetSinkInputInfoMuteStatusCb(pa_context *c, const pa_sink_input_info *i, int eol, void *userdata); + + static void GetSinkInputInfoCorkStatusCb(pa_context *c, const pa_sink_input_info *i, int eol, void *userdata); +private: + struct UserData { + PulseAudioPolicyManager* thiz; + AudioStreamType streamType; + float volume; + bool mute; + bool isCorked; + uint32_t idx; + }; + + PulseAudioPolicyManager() + : mContext(nullptr), + mMainLoop(nullptr), + mRingerMode(RINGER_MODE_NORMAL), + mAudioPolicyKvStore(nullptr) + { + mVolumeMap[STREAM_MUSIC] = MAX_VOLUME; + mVolumeMap[STREAM_RING] = MAX_VOLUME; + } + + virtual ~PulseAudioPolicyManager() {} + + bool ConnectToPulseAudio(void); + void HandleSinkInputEvent(pa_context *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata); + std::string GetModuleArgs(std::shared_ptr audioPortInfo); + std::string GetStreamNameByStreamType(AudioStreamType streamType); + AudioStreamType GetStreamIDByType(std::string streamType); + bool InitAudioPolicyKvStore(bool& isFirstBoot); + void InitVolumeMap(bool isFirstBoot); + bool LoadVolumeMap(void); + void WriteVolumeToKvStore(AudioStreamType streamType, float volume); + bool LoadVolumeFromKvStore(std::unique_ptr& audioPolicyKvStoreSnapshot, + AudioStreamType streamType); + void InitRingerMode(bool isFirstBoot); + bool LoadRingerMode(void); + void WriteRingerModeToKvStore(AudioRingerMode ringerMode); + + pa_context* mContext; + pa_threaded_mainloop* mMainLoop; + std::unordered_map mVolumeMap; + AudioRingerMode mRingerMode; + std::unique_ptr mAudioPolicyKvStore; +}; +} // namespace AudioStandard +} // namespace OHOS +#endif // ST_PULSEAUDIO_POLICY_MANAGER_H diff --git a/services/src/audio_policy/server/service/src/audio_policy_service.cpp b/services/src/audio_policy/server/service/src/audio_policy_service.cpp new file mode 100644 index 0000000000000000000000000000000000000000..936b21fcdee150c0d7de83a201f8e15ad391ae7f --- /dev/null +++ b/services/src/audio_policy/server/service/src/audio_policy_service.cpp @@ -0,0 +1,233 @@ +/* + * Copyright (C) 2021 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_errors.h" +#include "audio_policy_service.h" +#include "media_log.h" + +namespace OHOS { +namespace AudioStandard { +using namespace std; +bool AudioPolicyService::Init(void) +{ + mAudioPolicyManager.Init(); + if (!mConfigParser.LoadConfiguration()) { + MEDIA_ERR_LOG("Audio Config Load Configuration failed"); + return false; + } + if (!mConfigParser.Parse()) { + MEDIA_ERR_LOG("Audio Config Parse failed"); + return false; + } + + return true; +} + +void AudioPolicyService::Deinit(void) +{ + mAudioPolicyManager.CloseAudioPort(mIOHandles[HDI_SINK]); + mAudioPolicyManager.CloseAudioPort(mIOHandles[HDI_SOURCE]); + return; +} + +int32_t AudioPolicyService::SetStreamVolume(AudioStreamType streamType, float volume) const +{ + return mAudioPolicyManager.SetStreamVolume(streamType, volume); +} + +float AudioPolicyService::GetStreamVolume(AudioStreamType streamType) const +{ + return mAudioPolicyManager.GetStreamVolume(streamType); +} + +int32_t AudioPolicyService::SetStreamMute(AudioStreamType streamType, bool mute) const +{ + return mAudioPolicyManager.SetStreamMute(streamType, mute); +} + +bool AudioPolicyService::GetStreamMute(AudioStreamType streamType) const +{ + return mAudioPolicyManager.GetStreamMute(streamType); +} + +bool AudioPolicyService::IsStreamActive(AudioStreamType streamType) const +{ + return mAudioPolicyManager.IsStreamActive(streamType); +} + +int32_t AudioPolicyService::SetDeviceActive(DeviceType deviceType, bool active) +{ + MEDIA_DEBUG_LOG("SetDeviceActive - Policy Service: deviceType %d", deviceType); + + if (deviceType == DEVICE_TYPE_NONE) + return ERR_DEVICE_NOT_SUPPORTED; + + bool updateActiveDevices = true; + AudioIOHandle ioHandle = GetAudioIOHandle(deviceType); + list& activeDevices = GetActiveDevicesList(deviceType); + + if (!active) { + if (activeDevices.size() <= 1) { + MEDIA_ERR_LOG("[Policy Service] Only one Active device. So cannot deactivate!"); + return ERROR; + } + + for (list::const_iterator iter = activeDevices.begin(); + iter != activeDevices.end(); ++iter) { + if (*iter == deviceType) { + activeDevices.erase(iter); + } + } + + deviceType = activeDevices.front(); + updateActiveDevices = false; + } + + int32_t result = 0; + switch (deviceType) { + case BLUETOOTH_A2DP: + result = mAudioPolicyManager.SetDeviceActive(ioHandle, deviceType, BLUEZ_SINK, active); + break; + case SPEAKER: + result = mAudioPolicyManager.SetDeviceActive(ioHandle, deviceType, HDI_SINK, active); + break; + case MIC: + result = mAudioPolicyManager.SetDeviceActive(ioHandle, deviceType, HDI_SOURCE, active); + break; + case BLUETOOTH_SCO: + result = mAudioPolicyManager.SetDeviceActive(ioHandle, deviceType, BLUEZ_SOURCE, active); + break; + default: + result = ERR_DEVICE_NOT_SUPPORTED; + break; + } + + if (!result) { + if (updateActiveDevices) { + for (list::const_iterator iter = activeDevices.begin(); + iter != activeDevices.end(); ++iter) { + if (*iter == deviceType) { + activeDevices.erase(iter); + } + } + activeDevices.push_front(deviceType); + } + return SUCCESS; + } else { + MEDIA_ERR_LOG("SetDeviceActive - Policy Service: returned:%{public}d", result); + return ERROR; + } +} + +bool AudioPolicyService::IsDeviceActive(DeviceType deviceType) const +{ + bool result = false; + + switch (deviceType) { + case SPEAKER: + case BLUETOOTH_A2DP: + for (list::const_iterator iter = mActiveOutputDevices.begin(); + iter != mActiveOutputDevices.end(); ++iter) { + if (*iter == deviceType) { + result = true; + break; + } + } + break; + case MIC: + case BLUETOOTH_SCO: + for (list::const_iterator iter = mActiveInputDevices.begin(); + iter != mActiveInputDevices.end(); ++iter) { + if (*iter == deviceType) { + result = true; + break; + } + } + break; + default: + break; + } + + return result; +} + +int32_t AudioPolicyService::SetRingerMode(AudioRingerMode ringMode) +{ + return mAudioPolicyManager.SetRingerMode(ringMode); +} + +AudioRingerMode AudioPolicyService::GetRingerMode() const +{ + return mAudioPolicyManager.GetRingerMode(); +} + +// Parser callbacks + +void AudioPolicyService::OnAudioPortAvailable(shared_ptr portInfo) +{ + AudioIOHandle ioHandle = mAudioPolicyManager.OpenAudioPort(portInfo); + mIOHandles[portInfo->name] = ioHandle; + return; +} + +void AudioPolicyService::OnAudioPortPinAvailable(shared_ptr portInfo) +{ + return; +} + +void AudioPolicyService::OnDefaultOutputPortPin(DeviceType deviceType) +{ + AudioIOHandle ioHandle = GetAudioIOHandle(deviceType); + mAudioPolicyManager.SetDeviceActive(ioHandle, deviceType, HDI_SINK, true); + mActiveOutputDevices.push_front(deviceType); + MEDIA_DEBUG_LOG("OnDefaultOutputPortPin DeviceType: %{public}d", deviceType); + return; +} + +void AudioPolicyService::OnDefaultInputPortPin(DeviceType deviceType) +{ + MEDIA_DEBUG_LOG("OnDefaultInputPortPin DeviceType: %{public}d", deviceType); + AudioIOHandle ioHandle = GetAudioIOHandle(deviceType); + mAudioPolicyManager.SetDeviceActive(ioHandle, deviceType, HDI_SOURCE, true); + mActiveInputDevices.push_front(deviceType); + return; +} + +// private methods +AudioIOHandle AudioPolicyService::GetAudioIOHandle(DeviceType deviceType) +{ + AudioIOHandle ioHandle; + switch (deviceType) { + case SPEAKER: + case WIRED_HEADSET: + ioHandle = mIOHandles[HDI_SINK]; + break; + case BLUETOOTH_A2DP: + ioHandle = mIOHandles[BLUEZ_SINK]; + break; + case MIC: + ioHandle = mIOHandles[HDI_SOURCE]; + break; + case BLUETOOTH_SCO: + ioHandle = mIOHandles[BLUEZ_SOURCE]; + break; + default: + ioHandle = mIOHandles[HDI_SINK]; + break; + } + return ioHandle; +} +} +} diff --git a/services/src/audio_policy/server/service/src/config/xml_parser.cpp b/services/src/audio_policy/server/service/src/config/xml_parser.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5bb9e7d3e4690d1eda4050fc9077f2ca7c9ad23a --- /dev/null +++ b/services/src/audio_policy/server/service/src/config/xml_parser.cpp @@ -0,0 +1,239 @@ +/* + * Copyright (C) 2021 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 "media_log.h" +#include "xml_parser.h" + +namespace OHOS { +namespace AudioStandard { +bool XMLParser::LoadConfiguration() +{ + mDoc = xmlReadFile(CONFIG_FILE, NULL, 0); + if (mDoc == NULL) { + MEDIA_ERR_LOG("xmlReadFile Failed"); + return false; + } + + return true; +} + +bool XMLParser::Parse() +{ + xmlNode* root = xmlDocGetRootElement(mDoc); + if (root == NULL) { + MEDIA_ERR_LOG("xmlDocGetRootElement Failed"); + return false; + } + + if (!ParseInternal(root)) + return false; + + return true; +} + +void XMLParser::Destroy() +{ + if (mDoc != NULL) { + xmlFreeDoc(mDoc); + } + return; +} + +bool XMLParser::ParseInternal(xmlNode* node) +{ + xmlNode* currNode = node; + for (; currNode; currNode = currNode->next) { + if (XML_ELEMENT_NODE == currNode->type) { + switch (GetNodeNameAsInt(currNode)) { + case BUILT_IN_DEVICES: + ParseBuiltInDevices(currNode); + break; + case DEFAULT_OUTPUT_DEVICE: + ParseDefaultOutputDevice(currNode); + break; + case DEFAULT_INPUT_DEVICE: + ParseDefaultInputDevice(currNode); + break; + case AUDIO_PORTS: + ParseAudioPorts(currNode); + break; + case AUDIO_PORT_PINS: + ParseAudioPortPins(currNode); + break; + default: + ParseInternal(currNode->children); + break; + } + } + } + + return true; +} + +NodeName XMLParser::GetNodeNameAsInt(xmlNode* node) +{ + if (!xmlStrcmp(node->name, reinterpret_cast("BuiltInDevices"))) + return BUILT_IN_DEVICES; + if (!xmlStrcmp(node->name, reinterpret_cast("DefaultOutputDevice"))) + return DEFAULT_OUTPUT_DEVICE; + if (!xmlStrcmp(node->name, reinterpret_cast("DefaultInputDevice"))) + return DEFAULT_INPUT_DEVICE; + if (!xmlStrcmp(node->name, reinterpret_cast("AudioPorts"))) + return AUDIO_PORTS; + if (!xmlStrcmp(node->name, reinterpret_cast("AudioPort"))) + return AUDIO_PORT; + if (!xmlStrcmp(node->name, reinterpret_cast("AudioPortPins"))) + return AUDIO_PORT_PINS; + if (!xmlStrcmp(node->name, reinterpret_cast("AudioPortPin"))) + return AUDIO_PORT_PIN; + + return UNKNOWN; +} + +void XMLParser::ParseBuiltInDevices(xmlNode* node) +{ + while (node) { + xmlNode* child = node->children; + xmlChar* device = xmlNodeGetContent(child); + + if (device != NULL) { + MEDIA_DEBUG_LOG("Trigger Cb"); + } + + node = node->next; + } + return; +} + +void XMLParser::ParseDefaultOutputDevice(xmlNode* node) +{ + xmlNode* child = node->children; + xmlChar* device = xmlNodeGetContent(child); + + if (device != NULL) { + MEDIA_DEBUG_LOG("DefaultOutputDevice %{public}s", device); + mPortObserver.OnDefaultOutputPortPin(GetDeviceType(device)); + } + return; +} + + +void XMLParser::ParseDefaultInputDevice(xmlNode* node) +{ + xmlNode* child = node->children; + xmlChar* device = xmlNodeGetContent(child); + + MEDIA_DEBUG_LOG("DefaultInputDevice"); + if (device != NULL) { + MEDIA_DEBUG_LOG("DefaultInputDevice %{public}s", device); + mPortObserver.OnDefaultInputPortPin(GetDeviceType(device)); + } + return; +} + +void XMLParser::ParseAudioPorts(xmlNode* node) +{ + xmlNode* child = node->xmlChildrenNode; + + for (; child; child = child->next) { + if (!xmlStrcmp(child->name, reinterpret_cast("AudioPort"))) { + std::shared_ptr portInfo = std::make_shared(); + portInfo->type = TYPE_AUDIO_PORT; + + if (xmlHasProp(child, reinterpret_cast(const_cast("role")))) { + portInfo->role = reinterpret_cast(xmlGetProp( + child, + reinterpret_cast(const_cast("role")))); + } + + if (xmlHasProp(child, reinterpret_cast(const_cast("name")))) { + portInfo->name = reinterpret_cast(xmlGetProp( + child, + reinterpret_cast(const_cast("name")))); + } + + if (xmlHasProp(child, reinterpret_cast(const_cast("channels")))) { + portInfo->channels = reinterpret_cast(xmlGetProp( + child, + reinterpret_cast(const_cast("channels")))); + } + + if (xmlHasProp(child, reinterpret_cast(const_cast("buffer_size")))) { + portInfo->buffer_size = reinterpret_cast(xmlGetProp( + child, + reinterpret_cast(const_cast("buffer_size")))); + } + + if (xmlHasProp(child, reinterpret_cast(const_cast("rate")))) { + portInfo->rate = reinterpret_cast(xmlGetProp( + child, + reinterpret_cast(const_cast("rate")))); + } + + if (xmlHasProp(child, reinterpret_cast(const_cast("file")))) { + portInfo->fileName = reinterpret_cast(xmlGetProp( + child, + reinterpret_cast(const_cast("file")))); + } + + mPortObserver.OnAudioPortAvailable(portInfo); + } + } + + return; +} + +void XMLParser::ParseAudioPortPins(xmlNode* node) +{ + xmlNode* child = node->xmlChildrenNode; + + for (; child; child = child->next) { + if (!xmlStrcmp(child->name, reinterpret_cast("AudioPortPin"))) { + std::shared_ptr portInfo = std::make_shared(); + portInfo->type = TYPE_AUDIO_PORT_PIN; + + if (xmlHasProp(child, reinterpret_cast(const_cast("role")))) + portInfo->role = reinterpret_cast(xmlGetProp( + child, + reinterpret_cast(const_cast("role")))); + if (xmlHasProp(child, reinterpret_cast(const_cast("name")))) + portInfo->name = reinterpret_cast(xmlGetProp( + child, + reinterpret_cast(const_cast("name")))); + if (xmlHasProp(child, reinterpret_cast(const_cast("type")))) + portInfo->pinType = reinterpret_cast(xmlGetProp( + child, + reinterpret_cast(const_cast("type")))); + + MEDIA_INFO_LOG("AudioPort:Role: %s, Name: %s, Type: %s", portInfo->role, portInfo->name, portInfo->pinType); + + mPortObserver.OnAudioPortPinAvailable(portInfo); + } + } + + return; +} + +DeviceType XMLParser::GetDeviceType(xmlChar* device) +{ + if (!xmlStrcmp(device, reinterpret_cast("Speaker"))) + return SPEAKER; + if (!xmlStrcmp(device, reinterpret_cast("Built-In Mic"))) + return MIC; + return DEVICE_TYPE_NONE; +} +} // namespace AudioStandard +} // namespace OHOS diff --git a/services/src/audio_policy/server/service/src/manager/pulseaudio_policy_manager.cpp b/services/src/audio_policy/server/service/src/manager/pulseaudio_policy_manager.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8b43169d8edea2e95b9e49b0520187fb186823a3 --- /dev/null +++ b/services/src/audio_policy/server/service/src/manager/pulseaudio_policy_manager.cpp @@ -0,0 +1,982 @@ +/* + * Copyright (C) 2021 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 "audio_errors.h" +#include "media_log.h" +#include "pulseaudio_policy_manager.h" + +namespace OHOS { +namespace AudioStandard { +bool PulseAudioPolicyManager::Init() +{ + mMainLoop = pa_threaded_mainloop_new(); + if (!mMainLoop) { + MEDIA_ERR_LOG("[PolicyManager] MainLoop creation failed"); + return false; + } + + if (pa_threaded_mainloop_start(mMainLoop) < 0) { + MEDIA_ERR_LOG("[PolicyManager] Failed to start mainloop"); + pa_threaded_mainloop_free (mMainLoop); + return false; + } + + pa_threaded_mainloop_lock(mMainLoop); + + while (true) { + pa_context_state_t state; + + if (mContext != NULL) { + state = pa_context_get_state(mContext); + if (state == PA_CONTEXT_READY) + break; + // if pulseaudio is ready, retry connect to pulseaudio. before retry wait for sometime. reduce sleep later + usleep(PA_CONNECT_RETRY_SLEEP_IN_MICRO_SECONDS); + } + + bool result = ConnectToPulseAudio(); + if (!result || !PA_CONTEXT_IS_GOOD(pa_context_get_state(mContext))) { + continue; + } + + MEDIA_DEBUG_LOG("[PolicyManager] pa context not ready... wait"); + + // Wait for the context to be ready + pa_threaded_mainloop_wait(mMainLoop); + } + + pa_threaded_mainloop_unlock(mMainLoop); + + bool isFirstBoot = false; + InitAudioPolicyKvStore(isFirstBoot); + InitVolumeMap(isFirstBoot); + InitRingerMode(isFirstBoot); + return true; +} + +void PulseAudioPolicyManager::Deinit(void) +{ + if (mContext != NULL) { + pa_context_disconnect (mContext); + + /* Make sure we don't get any further callbacks */ + pa_context_set_state_callback (mContext, NULL, NULL); + pa_context_set_subscribe_callback (mContext, NULL, NULL); + + pa_context_unref (mContext); + } + + if (mMainLoop != NULL) { + pa_threaded_mainloop_stop (mMainLoop); + pa_threaded_mainloop_free (mMainLoop); + } + + return; +} + +int32_t PulseAudioPolicyManager::SetStreamVolume(AudioStreamType streamType, float volume) +{ + std::unique_ptr userData = std::make_unique(); + userData->thiz = this; + userData->volume = volume; + userData->streamType = streamType; + + if (mContext == NULL) { + MEDIA_ERR_LOG("[PolicyManager] mContext is nullptr"); + return ERROR; + } + + // Incase if KvStore didnot connect during bootup + if (mAudioPolicyKvStore == nullptr) { + bool isFirstBoot = false; + InitAudioPolicyKvStore(isFirstBoot); + } + + mVolumeMap[streamType] = volume; + WriteVolumeToKvStore(streamType, volume); + + pa_threaded_mainloop_lock(mMainLoop); + + pa_operation *operation = pa_context_get_sink_input_info_list(mContext, + PulseAudioPolicyManager::GetSinkInputInfoVolumeCb, reinterpret_cast(userData.get())); + if (operation == NULL) { + MEDIA_ERR_LOG("[PolicyManager] pa_context_get_sink_input_info_list returned nullptr"); + pa_threaded_mainloop_unlock(mMainLoop); + return ERROR; + } + userData.release(); + + while (pa_operation_get_state(operation) == PA_OPERATION_RUNNING) { + pa_threaded_mainloop_wait(mMainLoop); + } + + pa_operation_unref(operation); + pa_threaded_mainloop_unlock(mMainLoop); + return SUCCESS; +} + +float PulseAudioPolicyManager::GetStreamVolume(AudioStreamType streamType) +{ + return mVolumeMap[streamType]; +} + +int32_t PulseAudioPolicyManager::SetStreamMute(AudioStreamType streamType, bool mute) +{ + std::unique_ptr userData = std::make_unique(); + userData->thiz = this; + userData->mute = mute; + userData->streamType = streamType; + + if (mContext == NULL) { + MEDIA_ERR_LOG("[PolicyManager] mContext is nullptr"); + return ERROR; + } + + pa_threaded_mainloop_lock(mMainLoop); + + pa_operation* operation = pa_context_get_sink_input_info_list(mContext, + PulseAudioPolicyManager::GetSinkInputInfoMuteCb, reinterpret_cast(userData.get())); + if (operation == NULL) { + MEDIA_ERR_LOG("[PolicyManager] pa_context_get_sink_input_info_list returned nullptr"); + pa_threaded_mainloop_unlock(mMainLoop); + return ERROR; + } + + while (pa_operation_get_state(operation) == PA_OPERATION_RUNNING) { + pa_threaded_mainloop_wait(mMainLoop); + } + + pa_operation_unref(operation); + pa_threaded_mainloop_unlock(mMainLoop); + + return SUCCESS; +} + +bool PulseAudioPolicyManager::GetStreamMute(AudioStreamType streamType) +{ + std::unique_ptr userData = std::make_unique(); + userData->thiz = this; + userData->streamType = streamType; + userData->mute = false; + + if (mContext == NULL) { + MEDIA_ERR_LOG("[PolicyManager] mContext is nullptr"); + return false; + } + + pa_threaded_mainloop_lock(mMainLoop); + + pa_operation *operation = pa_context_get_sink_input_info_list(mContext, + PulseAudioPolicyManager::GetSinkInputInfoMuteStatusCb, reinterpret_cast(userData.get())); + if (operation == NULL) { + MEDIA_ERR_LOG("[PolicyManager] pa_context_get_sink_input_info_list returned nullptr"); + pa_threaded_mainloop_unlock(mMainLoop); + return false; + } + + while (pa_operation_get_state(operation) == PA_OPERATION_RUNNING) { + pa_threaded_mainloop_wait(mMainLoop); + } + + pa_operation_unref(operation); + pa_threaded_mainloop_unlock(mMainLoop); + + return (userData->mute) ? true : false; +} + +bool PulseAudioPolicyManager::IsStreamActive(AudioStreamType streamType) +{ + std::unique_ptr userData = std::make_unique(); + userData->thiz = this; + userData->streamType = streamType; + userData->isCorked = true; + + if (mContext == NULL) { + MEDIA_ERR_LOG("[PolicyManager] mContext is nullptr"); + return false; + } + + pa_threaded_mainloop_lock(mMainLoop); + + pa_operation *operation = pa_context_get_sink_input_info_list(mContext, + PulseAudioPolicyManager::GetSinkInputInfoCorkStatusCb, reinterpret_cast(userData.get())); + if (operation == NULL) { + MEDIA_ERR_LOG("[PolicyManager] pa_context_get_sink_input_info_list returned nullptr"); + pa_threaded_mainloop_unlock(mMainLoop); + return false; + } + + while (pa_operation_get_state(operation) == PA_OPERATION_RUNNING) { + pa_threaded_mainloop_wait(mMainLoop); + } + + pa_operation_unref(operation); + pa_threaded_mainloop_unlock(mMainLoop); + + MEDIA_INFO_LOG("[PolicyManager] cork for stream %s : %d", + GetStreamNameByStreamType(streamType).c_str(), userData->isCorked); + + return (userData->isCorked) ? false : true; +} + + +int32_t PulseAudioPolicyManager::SetDeviceActive(AudioIOHandle ioHandle, DeviceType deviceType, + std::string name, bool active) +{ + pa_threaded_mainloop_lock(mMainLoop); + + switch (deviceType) { + case SPEAKER: + case BLUETOOTH_A2DP: { + pa_operation* operation = pa_context_set_default_sink(mContext, name.c_str(), NULL, NULL); + if (operation == NULL) { + MEDIA_ERR_LOG("[PolicyManager] set default sink failed!"); + pa_threaded_mainloop_unlock(mMainLoop); + return ERR_OPERATION_FAILED; + } + pa_operation_unref(operation); + break; + } + case MIC: + case BLUETOOTH_SCO: { + pa_operation* operation = pa_context_set_default_source(mContext, name.c_str(), NULL, NULL); + if (operation == NULL) { + MEDIA_ERR_LOG("[PolicyManager] set default source failed!"); + pa_threaded_mainloop_unlock(mMainLoop); + return ERR_OPERATION_FAILED; + } + + pa_operation_unref(operation); + break; + } + default: + break; + } + + pa_threaded_mainloop_unlock(mMainLoop); + return SUCCESS; +} + +int32_t PulseAudioPolicyManager::SetRingerMode(AudioRingerMode ringerMode) +{ + mRingerMode = ringerMode; + + // Incase if KvStore didnot connect during bootup + if (mAudioPolicyKvStore == nullptr) { + bool isFirstBoot = false; + InitAudioPolicyKvStore(isFirstBoot); + } + + WriteRingerModeToKvStore(ringerMode); + return SUCCESS; +} + +AudioRingerMode PulseAudioPolicyManager::GetRingerMode() +{ + return mRingerMode; +} + +AudioIOHandle PulseAudioPolicyManager::OpenAudioPort(std::shared_ptr audioPortInfo) +{ + std::string moduleArgs = GetModuleArgs(audioPortInfo); + MEDIA_INFO_LOG("[PolicyManager] load-module %{public}s %{public}s", audioPortInfo->name, moduleArgs.c_str()); + + if (!strcmp(audioPortInfo->name, PIPE_SOURCE) || !strcmp(audioPortInfo->name, PIPE_SINK)) { + if (audioPortInfo->fileName != nullptr) { + if (access(audioPortInfo->fileName, F_OK) == 0) { + int32_t ret = std::remove(audioPortInfo->fileName); + if(ret) { + MEDIA_ERR_LOG("[PolicyManager] Error Removing file: %{public}s Failed! ret val: %{public}d", + audioPortInfo->fileName, ret); + } + } else { + MEDIA_DEBUG_LOG("[PolicyManager] File: %{public}s does not exist!", audioPortInfo->fileName); + } + } else { + MEDIA_ERR_LOG("[PolicyManager] Error audioPortInfo->fileName is null!"); + } + } + + pa_threaded_mainloop_lock(mMainLoop); + + std::unique_ptr userData = std::make_unique(); + userData->thiz = this; + + pa_operation* operation = pa_context_load_module(mContext, audioPortInfo->name, moduleArgs.c_str(), ModuleLoadCb, + reinterpret_cast(userData.get())); + if (operation == NULL) { + MEDIA_ERR_LOG("[PolicyManager] pa_context_load_module returned nullptr"); + pa_threaded_mainloop_unlock(mMainLoop); + return reinterpret_cast(ERR_INVALID_HANDLE); + } + + while (pa_operation_get_state(operation) == PA_OPERATION_RUNNING) { + pa_threaded_mainloop_wait(mMainLoop); + } + + pa_operation_unref(operation); + pa_threaded_mainloop_unlock(mMainLoop); + + AudioIOHandle ioHandle = reinterpret_cast(userData->idx); + return ioHandle; +} + +int32_t PulseAudioPolicyManager::CloseAudioPort(AudioIOHandle ioHandle) +{ + pa_threaded_mainloop_lock(mMainLoop); + + pa_operation* operation = pa_context_unload_module(mContext, reinterpret_cast(ioHandle), NULL, NULL); + if (operation == NULL) { + MEDIA_ERR_LOG("[PolicyManager] pa_context_unload_module returned nullptr!"); + pa_threaded_mainloop_unlock(mMainLoop); + return ERROR; + } + + pa_operation_unref(operation); + pa_threaded_mainloop_unlock(mMainLoop); + return SUCCESS; +} + +// Private Members +bool PulseAudioPolicyManager::ConnectToPulseAudio(void) +{ + if (mContext != NULL) { + pa_context_disconnect (mContext); + pa_context_set_state_callback (mContext, NULL, NULL); + pa_context_set_subscribe_callback (mContext, NULL, NULL); + + pa_context_unref (mContext); + } + + pa_proplist *proplist = pa_proplist_new(); + pa_proplist_sets(proplist, PA_PROP_APPLICATION_NAME, "PulseAudio Service"); + pa_proplist_sets(proplist, PA_PROP_APPLICATION_ID, "com.ohos.pulseaudio.service"); + mContext = pa_context_new_with_proplist(pa_threaded_mainloop_get_api(mMainLoop), NULL, proplist); + pa_proplist_free(proplist); + + if (mContext == NULL) { + MEDIA_ERR_LOG("[PolicyManager] creating pa context failed"); + return false; + } + + pa_context_set_state_callback(mContext, PulseAudioPolicyManager::ContextStateCb, this); + + if (pa_context_connect(mContext, NULL, PA_CONTEXT_NOFAIL, NULL) < 0) { + if (pa_context_errno(mContext) == PA_ERR_INVALID) { + MEDIA_ERR_LOG("[PolicyManager] pa context connect failed: %{public}s", + pa_strerror(pa_context_errno(mContext))); + goto Fail; + } + } + + return true; + +Fail: + /* Make sure we don't get any further callbacks */ + pa_context_set_state_callback (mContext, NULL, NULL); + pa_context_set_subscribe_callback (mContext, NULL, NULL); + pa_context_unref (mContext); + return false; +} + +std::string PulseAudioPolicyManager::GetModuleArgs(std::shared_ptr audioPortInfo) +{ + std::string args; + + if (!strcmp(audioPortInfo->name, HDI_SINK)) { + if (audioPortInfo->rate != nullptr) { + args = "rate="; + args.append(audioPortInfo->rate); + } + + if (audioPortInfo->channels != nullptr) { + args.append(" channels="); + args.append(audioPortInfo->channels); + } + + if (audioPortInfo->buffer_size != nullptr) { + args.append(" buffer_size="); + args.append(audioPortInfo->buffer_size); + } + } else if (!strcmp(audioPortInfo->name, HDI_SOURCE)) { + if (audioPortInfo->rate != nullptr) { + args = "rate="; + args.append(audioPortInfo->rate); + } + + if (audioPortInfo->channels != nullptr) { + args.append(" channels="); + args.append(audioPortInfo->channels); + } + + if (audioPortInfo->buffer_size != nullptr) { + args.append(" buffer_size="); + args.append(audioPortInfo->buffer_size); + } + } else if (!strcmp(audioPortInfo->name, PIPE_SINK)) { + if (audioPortInfo->fileName != nullptr) { + args = "file="; + args.append(audioPortInfo->fileName); + } + } else if (!strcmp(audioPortInfo->name, PIPE_SOURCE)) { + if (audioPortInfo->fileName != nullptr) { + args = "file="; + args.append(audioPortInfo->fileName); + } + } + + return args; +} + +std::string PulseAudioPolicyManager::GetStreamNameByStreamType(AudioStreamType streamType) +{ + switch (streamType) { + case STREAM_MUSIC: + return "music"; + case STREAM_RING: + return "ring"; + case STREAM_SYSTEM: + return "system"; + case STREAM_NOTIFICATION: + return "notification"; + case STREAM_ALARM: + return "alarm"; + case STREAM_DTMF: + return "dtmf"; + default: + return ""; + } +} + +AudioStreamType PulseAudioPolicyManager::GetStreamIDByType(std::string streamType) +{ + AudioStreamType stream = STREAM_MUSIC; + + if (!streamType.compare(std::string("music"))) + stream = STREAM_MUSIC; + else if (!streamType.compare(std::string("ring"))) + stream = STREAM_RING; + else if (!streamType.compare(std::string("system"))) + stream = STREAM_SYSTEM; + else if (!streamType.compare(std::string("notification"))) + stream = STREAM_NOTIFICATION; + else if (!streamType.compare(std::string("alarm"))) + stream = STREAM_ALARM; + + return stream; +} + +bool PulseAudioPolicyManager::InitAudioPolicyKvStore(bool& isFirstBoot) +{ + DistributedKvDataManager manager; + Options options; + + options.createIfMissing = false; + options.encrypt = false; + options.autoSync = true; + options.kvStoreType = KvStoreType::MULTI_VERSION; + + AppId appId; + appId.appId = "policymanager"; + StoreId storeId; + storeId.storeId = "audiopolicy"; + + // open and initialize kvstore instance. + if (mAudioPolicyKvStore == nullptr) { + manager.GetKvStore(options, appId, storeId, [&](Status status, std::unique_ptr kvStore) { + if (status == Status::STORE_NOT_FOUND) { + MEDIA_ERR_LOG("[PolicyManager] InitAudioPolicyKvStore: STORE_NOT_FOUND!"); + return; + } else { + mAudioPolicyKvStore = std::move(kvStore); + } + }); + } + + if (mAudioPolicyKvStore == nullptr) { + MEDIA_INFO_LOG("[PolicyManager] First Boot: Create AudioPolicyKvStore"); + options.createIfMissing = true; + // [create and] open and initialize kvstore instance. + manager.GetKvStore(options, appId, storeId, [&](Status status, std::unique_ptr kvStore) { + if (status != Status::SUCCESS) { + MEDIA_ERR_LOG("[PolicyManager] Create AudioPolicyKvStore Failed!"); + return; + } + + mAudioPolicyKvStore = std::move(kvStore); + isFirstBoot = true; + }); + } + + if (mAudioPolicyKvStore == nullptr) { + MEDIA_ERR_LOG("[PolicyManager] InitAudioPolicyKvStore: Failed!"); + return false; + } + + return true; +} + +void PulseAudioPolicyManager::InitVolumeMap(bool isFirstBoot) +{ + if (isFirstBoot == true) { + WriteVolumeToKvStore(STREAM_MUSIC, MAX_VOLUME); + WriteVolumeToKvStore(STREAM_RING, MAX_VOLUME); + MEDIA_INFO_LOG("[PolicyManager] Wrote default stream volumes to KvStore"); + } else { + LoadVolumeMap(); + } + return; +} + +void PulseAudioPolicyManager::InitRingerMode(bool isFirstBoot) +{ + if (mAudioPolicyKvStore == nullptr) { + MEDIA_ERR_LOG("[PolicyManager] mAudioPolicyKvStore is null!"); + return; + } + + if (isFirstBoot == true) { + mRingerMode = RINGER_MODE_NORMAL; + WriteRingerModeToKvStore(RINGER_MODE_NORMAL); + MEDIA_INFO_LOG("[PolicyManager] Wrote default ringer mode to KvStore"); + } else { + LoadRingerMode(); + } +} + +bool PulseAudioPolicyManager::LoadVolumeFromKvStore(std::unique_ptr& audioPolicyKvStoreSnapshot, + AudioStreamType streamType) +{ + Key key; + Value value; + + switch (streamType) { + case STREAM_MUSIC: + key = "music"; + break; + case STREAM_RING: + key = "ring"; + break; + default: + return false; + } + + Status status = audioPolicyKvStoreSnapshot->Get(key, value); + if (status == Status::SUCCESS) { + float volume = TransferByteArrayToType(value.Data()); + mVolumeMap[streamType] = volume; + MEDIA_DEBUG_LOG("[PolicyManager] volume from kvStore %{public}f for streamType:%{public}d", + volume, streamType); + return true; + } + + return false; +} + +bool PulseAudioPolicyManager::LoadVolumeMap(void) +{ + if (mAudioPolicyKvStore == nullptr) { + MEDIA_ERR_LOG("[PolicyManager] LoadVolumeMap: mAudioPolicyKvStore is null!"); + return false; + } + + std::unique_ptr audioPolicyKvStoreSnapshot; + + // open and initialize kvstore snapshot instance. + mAudioPolicyKvStore->GetKvStoreSnapshot(nullptr, + [&](Status status, std::unique_ptr kvStoreSnapshot) { + audioPolicyKvStoreSnapshot = std::move(kvStoreSnapshot); + }); + if (audioPolicyKvStoreSnapshot == nullptr) { + MEDIA_ERR_LOG("[PolicyManager] LoadVolumeMap: audioPolicyKvStoreSnapshot is null!"); + return false; + } + + if (!LoadVolumeFromKvStore(audioPolicyKvStoreSnapshot, STREAM_MUSIC)) + MEDIA_ERR_LOG("[PolicyManager] LoadVolumeMap: Couldnot load volume for Music from kvStore!"); + + if (!LoadVolumeFromKvStore(audioPolicyKvStoreSnapshot, STREAM_RING)) + MEDIA_ERR_LOG("[PolicyManager] LoadVolumeMap: Couldnot load volume for Ring from kvStore!"); + + mAudioPolicyKvStore->ReleaseKvStoreSnapshot(std::move(audioPolicyKvStoreSnapshot)); + return true; +} + +bool PulseAudioPolicyManager::LoadRingerMode(void) +{ + if (mAudioPolicyKvStore == nullptr) { + MEDIA_ERR_LOG("[PolicyManager] LoadRingerMap: mAudioPolicyKvStore is null!"); + return false; + } + + std::unique_ptr audioPolicyKvStoreSnapshot; + + // open and initialize kvstore snapshot instance. + mAudioPolicyKvStore->GetKvStoreSnapshot(nullptr, + [&](Status status, std::unique_ptr kvStoreSnapshot) { + audioPolicyKvStoreSnapshot = std::move(kvStoreSnapshot); + }); + if (audioPolicyKvStoreSnapshot == nullptr) { + MEDIA_ERR_LOG("[PolicyManager] LoadRingerMap: audioPolicyKvStoreSnapshot is null!"); + return false; + } + + // get ringer mode value from kvstore. + Key key = "ringermode"; + Value value; + Status status = audioPolicyKvStoreSnapshot->Get(key, value); + if (status == Status::SUCCESS) { + mRingerMode = static_cast(TransferByteArrayToType(value.Data())); + MEDIA_DEBUG_LOG("[PolicyManager] Ringer Mode from kvStore %{public}d", mRingerMode); + } + + mAudioPolicyKvStore->ReleaseKvStoreSnapshot(std::move(audioPolicyKvStoreSnapshot)); + return true; +} + +void PulseAudioPolicyManager::WriteVolumeToKvStore(AudioStreamType streamType, float volume) +{ + if (mAudioPolicyKvStore == nullptr) + return; + + Key key = GetStreamNameByStreamType(streamType); + Value value = Value(TransferTypeToByteArray(volume)); + + Status status = mAudioPolicyKvStore->Put(key, value); + if (status == Status::SUCCESS) { + MEDIA_INFO_LOG("[PolicyManager] volume %{public}f for %{public}s updated in kvStore", volume, + GetStreamNameByStreamType(streamType).c_str()); + } else { + MEDIA_ERR_LOG("[PolicyManager] volume %{public}f for %{public}s failed to update in kvStore!", volume, + GetStreamNameByStreamType(streamType).c_str()); + } + + return; +} + +void PulseAudioPolicyManager::WriteRingerModeToKvStore(AudioRingerMode ringerMode) +{ + if (mAudioPolicyKvStore == nullptr) + return; + + Key key = "ringermode"; + Value value = Value(TransferTypeToByteArray(ringerMode)); + + Status status = mAudioPolicyKvStore->Put(key, value); + if (status == Status::SUCCESS) { + MEDIA_INFO_LOG("[PolicyManager] Wrote RingerMode:%d to kvStore", ringerMode); + } else { + MEDIA_ERR_LOG("[PolicyManager] Writing RingerMode:%d to kvStore failed!", ringerMode); + } + + return; +} + +void PulseAudioPolicyManager::HandleSinkInputEvent(pa_context *c, pa_subscription_event_type_t t, uint32_t idx, + void *userdata) +{ + std::unique_ptr userData = std::make_unique(); + userData->thiz = this; + + if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) { + pa_operation* operation = pa_context_get_sink_input_info(c, idx, + PulseAudioPolicyManager::GetSinkInputInfoVolumeCb, reinterpret_cast(userData.get())); + if (operation == NULL) { + MEDIA_ERR_LOG("[PolicyManager] pa_context_get_sink_input_info_list returned nullptr"); + return; + } + userData.release(); + + pa_operation_unref(operation); + } + + return; +} + +void PulseAudioPolicyManager::ContextStateCb(pa_context *c, void *userdata) +{ + PulseAudioPolicyManager* thiz = reinterpret_cast(userdata); + + MEDIA_DEBUG_LOG("[PolicyManager] ContextStateCb %d", pa_context_get_state(c)); + + switch (pa_context_get_state(c)) { + case PA_CONTEXT_UNCONNECTED: + case PA_CONTEXT_CONNECTING: + case PA_CONTEXT_AUTHORIZING: + case PA_CONTEXT_SETTING_NAME: + break; + + case PA_CONTEXT_READY: { + pa_context_set_subscribe_callback(c, PulseAudioPolicyManager::SubscribeCb, thiz); + + pa_operation* operation = pa_context_subscribe(c, (pa_subscription_mask_t) + (PA_SUBSCRIPTION_MASK_SINK | PA_SUBSCRIPTION_MASK_SOURCE | + PA_SUBSCRIPTION_MASK_SINK_INPUT | PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT | + PA_SUBSCRIPTION_MASK_CARD), NULL, NULL); + if (operation == NULL) { + pa_threaded_mainloop_signal(thiz->mMainLoop, 0); + return; + } + pa_operation_unref(operation); + pa_threaded_mainloop_signal(thiz->mMainLoop, 0); + break; + } + + case PA_CONTEXT_FAILED: + pa_threaded_mainloop_signal(thiz->mMainLoop, 0); + return; + + case PA_CONTEXT_TERMINATED: + default: + return; + } +} + +void PulseAudioPolicyManager::SubscribeCb(pa_context *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) +{ + PulseAudioPolicyManager* thiz = reinterpret_cast(userdata); + + switch (t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) { + case PA_SUBSCRIPTION_EVENT_SINK: + break; + + case PA_SUBSCRIPTION_EVENT_SOURCE: + break; + + case PA_SUBSCRIPTION_EVENT_SINK_INPUT: + thiz->HandleSinkInputEvent(c, t, idx, userdata); + break; + + case PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT: + break; + + default: + break; + } + + return; +} + +void PulseAudioPolicyManager::ModuleLoadCb(pa_context *c, uint32_t idx, void *userdata) +{ + UserData *userData = reinterpret_cast(userdata); + + if (idx == PA_INVALID_INDEX) { + MEDIA_ERR_LOG("[PolicyManager] Failure: %s", pa_strerror(pa_context_errno(c))); + userData->idx = PA_INVALID_INDEX; + } else { + userData->idx = idx; + } + pa_threaded_mainloop_signal(userData->thiz->mMainLoop, 0); + + return; +} + +void PulseAudioPolicyManager::GetSinkInputInfoVolumeCb(pa_context *c, const pa_sink_input_info *i, int eol, + void *userdata) +{ + UserData* userData = reinterpret_cast(userdata); + PulseAudioPolicyManager* thiz = userData->thiz; + + MEDIA_ERR_LOG("[PolicyManager] GetSinkInputInfoVolumeCb"); + if (eol < 0) { + delete userData; + MEDIA_ERR_LOG("[PolicyManager] Failed to get sink input information: %s", pa_strerror(pa_context_errno(c))); + return; + } + + if (eol) { + pa_threaded_mainloop_signal(thiz->mMainLoop, 0); + delete userData; + return; + } + + if (i->proplist == NULL) { + MEDIA_ERR_LOG("[PolicyManager] Invalid Proplist for sink input (%{public}d).", i->index); + return; + } + + const char *streamtype = pa_proplist_gets(i->proplist, "stream.type"); + if (streamtype == NULL) { + MEDIA_ERR_LOG("[PolicyManager] Invalid StreamType."); + return; + } + + std::string streamType(streamtype); + + AudioStreamType streamID = thiz->GetStreamIDByType(streamType); + float vol = thiz->mVolumeMap[streamID]; + + if (thiz->mRingerMode != RINGER_MODE_NORMAL) { + if (!streamType.compare("ring")) { + vol = MIN_VOLUME; + } + } + + pa_cvolume cv = i->volume; + int32_t volume = pa_sw_volume_from_linear(vol); + pa_cvolume_set(&cv, i->channel_map.channels, volume); + pa_operation_unref(pa_context_set_sink_input_volume(c, i->index, &cv, NULL, NULL)); + + if (streamID == userData->streamType) { + if (i->mute) { + pa_operation_unref(pa_context_set_sink_input_mute(c, i->index, 0, NULL, NULL)); + } + } + + MEDIA_INFO_LOG("[PolicyManager] Applied volume : %{public}f for stream : %{public}s, volumeInt%{public}d", + vol, i->name, volume); + + return; +} + +void PulseAudioPolicyManager::GetSinkInputInfoCb(pa_context *c, const pa_sink_input_info *i, int eol, void *userdata) +{ + UserData* userData = reinterpret_cast(userdata); + + if (eol < 0) { + MEDIA_ERR_LOG("[PolicyManager] Failed to get sink input information: %{public}s", + pa_strerror(pa_context_errno(c))); + return; + } + + pa_operation* operation = pa_context_move_sink_input_by_index(c, i->index, userData->idx, NULL, NULL); + if (operation == NULL) { + MEDIA_ERR_LOG("[PolicyManager] Moving Sink Input %{public}d to Sink %{public}d Failed", + i->index, userData->idx); + } + MEDIA_ERR_LOG("[PolicyManager] Moving Sink Input %{public}d to Sink %{public}d", i->index, userData->idx); + + if (eol) { + MEDIA_DEBUG_LOG("[PolicyManager] All inputs moved"); + pa_threaded_mainloop_signal(userData->thiz->mMainLoop, 0); + return; + } + + return; +} + +void PulseAudioPolicyManager::GetSinkInputInfoMuteCb(pa_context *c, const pa_sink_input_info *i, + int eol, void *userdata) +{ + UserData* userData = reinterpret_cast(userdata); + PulseAudioPolicyManager* thiz = userData->thiz; + + if (eol < 0) { + MEDIA_ERR_LOG("[PolicyManager] Failed to get sink input information: %s", pa_strerror(pa_context_errno(c))); + return; + } + + if (eol) { + pa_threaded_mainloop_signal(thiz->mMainLoop, 0); + return; + } + + if (i->proplist == NULL) { + MEDIA_ERR_LOG("[PolicyManager] Invalid Proplist for sink input (%{public}d).", i->index); + return; + } + + const char *streamtype = pa_proplist_gets(i->proplist, "stream.type"); + if (streamtype == NULL) { + MEDIA_ERR_LOG("[PolicyManager] Invalid StreamType."); + return; + } + + std::string streamType(streamtype); + if (!streamType.compare(thiz->GetStreamNameByStreamType(userData->streamType))) { + pa_operation_unref(pa_context_set_sink_input_mute(c, i->index, (userData->mute) ? 1 : 0, NULL, NULL)); + + MEDIA_INFO_LOG("[PolicyManager] Applied Mute : %{public}d for stream : %{public}s", userData->mute, i->name); + } + + return; +} + +void PulseAudioPolicyManager::GetSinkInputInfoMuteStatusCb(pa_context *c, const pa_sink_input_info *i, int eol, + void *userdata) +{ + UserData* userData = reinterpret_cast(userdata); + PulseAudioPolicyManager* thiz = userData->thiz; + + if (eol < 0) { + MEDIA_ERR_LOG("[PolicyManager] Failed to get sink input information: %s", pa_strerror(pa_context_errno(c))); + return; + } + + if (eol) { + pa_threaded_mainloop_signal(thiz->mMainLoop, 0); + return; + } + + if (i->proplist == NULL) { + MEDIA_ERR_LOG("[PolicyManager] Invalid Proplist for sink input (%{public}d).", i->index); + return; + } + + const char *streamtype = pa_proplist_gets(i->proplist, "stream.type"); + if (streamtype == NULL) { + MEDIA_ERR_LOG("[PolicyManager] Invalid StreamType."); + return; + } + + std::string streamType(streamtype); + if (!streamType.compare(thiz->GetStreamNameByStreamType(userData->streamType))) { + userData->mute = i->mute; + MEDIA_INFO_LOG("[PolicyManager] Mute : %{public}d for stream : %{public}s", userData->mute, i->name); + } + + return; +} + +void PulseAudioPolicyManager::GetSinkInputInfoCorkStatusCb(pa_context *c, const pa_sink_input_info *i, int eol, + void *userdata) +{ + UserData* userData = reinterpret_cast(userdata); + PulseAudioPolicyManager* thiz = userData->thiz; + + if (eol < 0) { + MEDIA_ERR_LOG("[PolicyManager] Failed to get sink input information: %s", pa_strerror(pa_context_errno(c))); + return; + } + + if (eol) { + pa_threaded_mainloop_signal(thiz->mMainLoop, 0); + return; + } + + if (i->proplist == NULL) { + MEDIA_ERR_LOG("[PolicyManager] Invalid Proplist for sink input (%{public}d).", i->index); + return; + } + + const char *streamtype = pa_proplist_gets(i->proplist, "stream.type"); + if (streamtype == NULL) { + MEDIA_ERR_LOG("[PolicyManager] Invalid StreamType."); + return; + } + + std::string streamType(streamtype); + if (!streamType.compare(thiz->GetStreamNameByStreamType(userData->streamType))) { + userData->isCorked = i->corked; + MEDIA_INFO_LOG("[PolicyManager] corked : %{public}d for stream : %{public}s", userData->isCorked, i->name); + } + + return; +} +} // namespace AudioStandard +} // namespace OHOS diff --git a/services/src/client/audio_manager_proxy.cpp b/services/src/client/audio_manager_proxy.cpp old mode 100755 new mode 100644 index fc7389a6dfdaad200b62b86669184525256a2db6..81627e9450018339309c5cf095128032b64cb355 --- a/services/src/client/audio_manager_proxy.cpp +++ b/services/src/client/audio_manager_proxy.cpp @@ -14,72 +14,77 @@ */ #include "audio_manager_proxy.h" -#include "audio_svc_manager.h" +#include "audio_system_manager.h" #include "media_log.h" namespace OHOS { +namespace AudioStandard { AudioManagerProxy::AudioManagerProxy(const sptr &impl) : IRemoteProxy(impl) { } -void AudioManagerProxy::SetVolume(AudioSvcManager::AudioVolumeType volumeType, int32_t volume) +float AudioManagerProxy::GetMaxVolume(AudioSystemManager::AudioVolumeType volumeType) { MessageParcel data; MessageParcel reply; MessageOption option; data.WriteInt32(static_cast(volumeType)); - data.WriteInt32(volume); - int error = Remote()->SendRequest(SET_VOLUME, data, reply, option); + int32_t error = Remote()->SendRequest(GET_MAX_VOLUME, data, reply, option); if (error != ERR_NONE) { - MEDIA_ERR_LOG("set volume failed, error: %d", error); - return; + MEDIA_ERR_LOG("Get max volume failed, error: %d", error); + return error; } + float volume = reply.ReadFloat(); + return volume; } -int32_t AudioManagerProxy::GetVolume(AudioSvcManager::AudioVolumeType volumeType) +float AudioManagerProxy::GetMinVolume(AudioSystemManager::AudioVolumeType volumeType) { MessageParcel data; MessageParcel reply; MessageOption option; - data.WriteInt32(static_cast(volumeType)); - int error = Remote()->SendRequest(GET_VOLUME, data, reply, option); + data.WriteInt32(static_cast(volumeType)); + + int32_t error = Remote()->SendRequest(GET_MIN_VOLUME, data, reply, option); if (error != ERR_NONE) { - MEDIA_ERR_LOG("Get volume failed, error: %d", error); + MEDIA_ERR_LOG("Get min volume failed, error: %d", error); return error; } - int volume = reply.ReadInt32(); + + float volume = reply.ReadFloat(); return volume; } -int32_t AudioManagerProxy::GetMaxVolume(AudioSvcManager::AudioVolumeType volumeType) +int32_t AudioManagerProxy::SetMicrophoneMute(bool isMute) { MessageParcel data; MessageParcel reply; MessageOption option; - data.WriteInt32(static_cast(volumeType)); - int error = Remote()->SendRequest(GET_MAX_VOLUME, data, reply, option); + data.WriteBool(isMute); + int32_t error = Remote()->SendRequest(SET_MICROPHONE_MUTE, data, reply, option); if (error != ERR_NONE) { - MEDIA_ERR_LOG("Get max volume failed, error: %d", error); + MEDIA_ERR_LOG("SetMicrophoneMute failed, error: %d", error); return error; } - int volume = reply.ReadInt32(); - return volume; + int32_t result = reply.ReadInt32(); + + return result; } -int32_t AudioManagerProxy::GetMinVolume(AudioSvcManager::AudioVolumeType volumeType) +bool AudioManagerProxy::IsMicrophoneMute() { MessageParcel data; MessageParcel reply; MessageOption option; - data.WriteInt32(static_cast(volumeType)); - int error = Remote()->SendRequest(GET_MIN_VOLUME, data, reply, option); + int32_t error = Remote()->SendRequest(IS_MICROPHONE_MUTE, data, reply, option); if (error != ERR_NONE) { - MEDIA_ERR_LOG("Get min volume failed, error: %d", error); - return error; + MEDIA_ERR_LOG("IsMicrophoneMute failed, error: %d", error); + return false; } - int volume = reply.ReadInt32(); - return volume; + bool isMute = reply.ReadBool(); + + return isMute; } std::vector> AudioManagerProxy::GetDevices(AudioDeviceDescriptor::DeviceFlag deviceFlag) @@ -87,17 +92,53 @@ std::vector> AudioManagerProxy::GetDevices(AudioDevi MessageParcel data; MessageParcel reply; MessageOption option; - data.WriteInt32(static_cast(deviceFlag)); - int error = Remote()->SendRequest(GET_DEVICES, data, reply, option); + data.WriteInt32(static_cast(deviceFlag)); + + int32_t error = Remote()->SendRequest(GET_DEVICES, data, reply, option); std::vector> deviceInfo; if (error != ERR_NONE) { - MEDIA_ERR_LOG("Get device failed, error: %d", error); + MEDIA_ERR_LOG("Get devices failed, error: %d", error); return deviceInfo; } - int size = reply.ReadInt32(); - for (int i = 0; i < size; i++) { + + int32_t size = reply.ReadInt32(); + for (int32_t i = 0; i < size; i++) { deviceInfo.push_back(AudioDeviceDescriptor::Unmarshalling(reply)); } return deviceInfo; } + +const std::string AudioManagerProxy::GetAudioParameter(const std::string key) +{ + MessageParcel data; + MessageParcel reply; + MessageOption option; + + data.WriteString(static_cast(key)); + int32_t error = Remote()->SendRequest(GET_AUDIO_PARAMETER, data, reply, option); + if (error != ERR_NONE) { + MEDIA_ERR_LOG("Get audio parameter failed, error: %d", error); + const std::string value = ""; + return value; + } + + const std::string value = reply.ReadString(); + return value; +} + +void AudioManagerProxy::SetAudioParameter(const std::string key, const std::string value) +{ + MessageParcel data; + MessageParcel reply; + MessageOption option; + + data.WriteString(static_cast(key)); + data.WriteString(static_cast(value)); + int32_t error = Remote()->SendRequest(SET_AUDIO_PARAMETER, data, reply, option); + if (error != ERR_NONE) { + MEDIA_ERR_LOG("Get audio parameter failed, error: %d", error); + return; + } +} +} // namespace AudioStandard } // namespace OHOS diff --git a/services/src/client/audio_service_client.cpp b/services/src/client/audio_service_client.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a14230a1281962d5d4522315cc763a5b21b09fe5 --- /dev/null +++ b/services/src/client/audio_service_client.cpp @@ -0,0 +1,1086 @@ +/* + * Copyright (C) 2021 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_service_client.h" +#include "media_log.h" +#include "securec.h" + +using namespace std; + +namespace OHOS { +namespace AudioStandard { +AudioRendererCallbacks::~AudioRendererCallbacks() = default; +AudioRecorderCallbacks::~AudioRecorderCallbacks() = default; + +const uint64_t LATENCY_IN_MSEC = 200UL; + +#define CHECK_AND_RETURN_IFINVALID(expr) \ + if (!(expr)) { \ + return AUDIO_CLIENT_ERR; \ + } + +#define CHECK_PA_STATUS_RET_IF_FAIL(mainLoop, context, paStream, error) \ + if (!context || !paStream || !mainLoop \ + || !PA_CONTEXT_IS_GOOD(pa_context_get_state(context)) \ + || !PA_STREAM_IS_GOOD(pa_stream_get_state(paStream))) { \ + return error; \ + } + +#define CHECK_PA_STATUS_FOR_WRITE(mainLoop, context, paStream, pError, retVal) \ + if (!context || !paStream || !mainLoop \ + || !PA_CONTEXT_IS_GOOD(pa_context_get_state(context)) \ + || !PA_STREAM_IS_GOOD(pa_stream_get_state(paStream))) { \ + pError = pa_context_errno(context); \ + return retVal; \ + } + +AudioStreamParams AudioServiceClient::ConvertFromPAAudioParams(pa_sample_spec paSampleSpec) +{ + AudioStreamParams audioParams; + + audioParams.channels = paSampleSpec.channels; + audioParams.samplingRate = paSampleSpec.rate; + + switch (paSampleSpec.format) { + case PA_SAMPLE_U8: + audioParams.format = SAMPLE_U8; + break; + case PA_SAMPLE_S16LE: + audioParams.format = SAMPLE_S16LE; + break; + case PA_SAMPLE_S24LE: + audioParams.format = SAMPLE_S24LE; + break; + case PA_SAMPLE_S32LE: + audioParams.format = SAMPLE_S32LE; + break; + default: + audioParams.format = INVALID_WIDTH; + break; + } + + return audioParams; +} + +pa_sample_spec AudioServiceClient::ConvertToPAAudioParams(AudioStreamParams audioParams) +{ + pa_sample_spec paSampleSpec; + + paSampleSpec.channels = audioParams.channels; + paSampleSpec.rate = audioParams.samplingRate; + + switch ((AudioSampleFormat)audioParams.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; +} + +static size_t AlignToAudioFrameSize(size_t l, const pa_sample_spec &ss) +{ + size_t fs; + + fs = pa_frame_size(&ss); + if (fs == 0) { + MEDIA_ERR_LOG(" Error: pa_frame_size returned 0"); + return 0; + } + + return (l / fs) * fs; +} + +void AudioServiceClient::PAStreamCmdSuccessCb(pa_stream *stream, int32_t success, void *userdata) +{ + AudioServiceClient *asClient = (AudioServiceClient *)userdata; + pa_threaded_mainloop *mainLoop = (pa_threaded_mainloop *) asClient->mainLoop; + + asClient->streamCmdStatus = success; + pa_threaded_mainloop_signal(mainLoop, 0); +} + +void AudioServiceClient::PAStreamRequestCb(pa_stream *stream, size_t length, void *userdata) +{ + pa_threaded_mainloop *mainLoop = (pa_threaded_mainloop *) userdata; + pa_threaded_mainloop_signal(mainLoop, 0); +} + +void AudioServiceClient::PAStreamUnderFlowCb(pa_stream *stream, void *userdata) +{ + AudioServiceClient *asClient = (AudioServiceClient *)userdata; + asClient->underFlowCount++; +} + +void AudioServiceClient::PAStreamLatencyUpdateCb(pa_stream *stream, void *userdata) +{ + pa_threaded_mainloop *mainLoop = (pa_threaded_mainloop *) userdata; + MEDIA_INFO_LOG("Inside latency update callback"); + pa_threaded_mainloop_signal(mainLoop, 0); +} + +void AudioServiceClient::PAStreamStateCb(pa_stream *stream, void *userdata) +{ + AudioServiceClient *asClient = (AudioServiceClient *)userdata; + pa_threaded_mainloop *mainLoop = (pa_threaded_mainloop *) asClient->mainLoop; + + if (asClient->mAudioRendererCallbacks) + asClient->mAudioRendererCallbacks->OnStreamStateChangeCb(); + + MEDIA_INFO_LOG("Current Stream State: %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(mainLoop, 0); + break; + + case PA_STREAM_UNCONNECTED: + case PA_STREAM_CREATING: + default: + break; + } +} + +void AudioServiceClient::PAContextStateCb(pa_context *context, void *userdata) +{ + pa_threaded_mainloop *mainLoop = (pa_threaded_mainloop *) userdata; + + MEDIA_INFO_LOG("Current Context State: %d", pa_context_get_state(context)); + + switch (pa_context_get_state(context)) { + case PA_CONTEXT_READY: + 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; + } +} + +AudioServiceClient::AudioServiceClient() +{ + isMainLoopStarted = false; + isContextConnected = false; + isStreamConnected = false; + + sinkDevices.clear(); + sourceDevices.clear(); + sinkInputs.clear(); + sourceOutputs.clear(); + clientInfo.clear(); + + mAudioRendererCallbacks = NULL; + mAudioRecorderCallbacks = NULL; + internalReadBuffer = NULL; + mainLoop = NULL; + paStream = NULL; + context = NULL; + api = NULL; + + internalRdBufIndex = 0; + internalRdBufLen = 0; + underFlowCount = 0; + + acache.readIndex = 0; + acache.writeIndex = 0; + acache.isFull = false; + acache.totalCacheSize = 0; + acache.buffer = NULL; +} + +void AudioServiceClient::ResetPAAudioClient() +{ + if (mainLoop && (isMainLoopStarted == true)) + pa_threaded_mainloop_stop(mainLoop); + + if (paStream) { + pa_stream_set_state_callback(paStream, NULL, NULL); + pa_stream_set_write_callback(paStream, NULL, NULL); + pa_stream_set_read_callback(paStream, NULL, NULL); + pa_stream_set_latency_update_callback(paStream, NULL, NULL); + pa_stream_set_underflow_callback(paStream, NULL, NULL); + + if (isStreamConnected == true) + pa_stream_disconnect(paStream); + pa_stream_unref(paStream); + } + + if (context) { + pa_context_set_state_callback(context, NULL, NULL); + if (isContextConnected == true) + pa_context_disconnect(context); + pa_context_unref(context); + } + + if (mainLoop) + pa_threaded_mainloop_free(mainLoop); + + isMainLoopStarted = false; + isContextConnected = false; + isStreamConnected = false; + + sinkDevices.clear(); + sourceDevices.clear(); + sinkInputs.clear(); + sourceOutputs.clear(); + clientInfo.clear(); + + mAudioRendererCallbacks = NULL; + mAudioRecorderCallbacks = NULL; + internalReadBuffer = NULL; + + mainLoop = NULL; + paStream = NULL; + context = NULL; + api = NULL; + + internalRdBufIndex = 0; + internalRdBufLen = 0; + underFlowCount = 0; + + acache.buffer = NULL; + acache.readIndex = 0; + acache.writeIndex = 0; + acache.isFull = false; + acache.totalCacheSize = 0; +} + +AudioServiceClient::~AudioServiceClient() +{ + ResetPAAudioClient(); +} + +int32_t AudioServiceClient::Initialize(ASClientType eClientType) +{ + int error = PA_ERR_INTERNAL; + eAudioClientType = eClientType; + + mainLoop = pa_threaded_mainloop_new(); + if (mainLoop == NULL) + return AUDIO_CLIENT_INIT_ERR; + + api = pa_threaded_mainloop_get_api(mainLoop); + if (api == NULL) { + ResetPAAudioClient(); + return AUDIO_CLIENT_INIT_ERR; + } + + context = pa_context_new(api, "AudioServiceClient"); + if (context == NULL) { + ResetPAAudioClient(); + return AUDIO_CLIENT_INIT_ERR; + } + + pa_context_set_state_callback(context, PAContextStateCb, mainLoop); + + if (pa_context_connect(context, NULL, PA_CONTEXT_NOFAIL, NULL) < 0) { + error = pa_context_errno(context); + ResetPAAudioClient(); + return AUDIO_CLIENT_INIT_ERR; + } + + isContextConnected = true; + pa_threaded_mainloop_lock(mainLoop); + + if (pa_threaded_mainloop_start(mainLoop) < 0) { + pa_threaded_mainloop_unlock(mainLoop); + ResetPAAudioClient(); + return AUDIO_CLIENT_INIT_ERR; + } + + isMainLoopStarted = true; + while (true) { + pa_context_state_t state = pa_context_get_state(context); + if (state == PA_CONTEXT_READY) + break; + + if (!PA_CONTEXT_IS_GOOD(state)) { + error = pa_context_errno(context); + // log the error + pa_threaded_mainloop_unlock(mainLoop); + ResetPAAudioClient(); + return AUDIO_CLIENT_INIT_ERR; + } + + pa_threaded_mainloop_wait(mainLoop); + } + + pa_threaded_mainloop_unlock(mainLoop); + return AUDIO_CLIENT_SUCCESS; +} + +const std::string AudioServiceClient::GetStreamName(AudioStreamType audioType) +{ + std::string name; + switch (audioType) { + case STREAM_VOICE_CALL: + name = "voice_call"; + break; + case STREAM_SYSTEM: + name = "system"; + break; + case STREAM_RING: + name = "ring"; + break; + case STREAM_MUSIC: + name = "music"; + break; + case STREAM_ALARM: + name = "alarm"; + break; + case STREAM_NOTIFICATION: + name = "notification"; + break; + case STREAM_BLUETOOTH_SCO: + name = "bluetooth_sco"; + break; + case STREAM_DTMF: + name = "dtmf"; + break; + case STREAM_TTS: + name = "tts"; + break; + case STREAM_ACCESSIBILITY: + name = "accessibility"; + break; + default: + name = "unknown"; + } + + const std::string streamName = name; + return streamName; +} + +int32_t AudioServiceClient::ConnectStreamToPA() +{ + int error, result; + + CHECK_AND_RETURN_IFINVALID(mainLoop && context && paStream); + pa_threaded_mainloop_lock(mainLoop); + + pa_buffer_attr bufferAttr; + bufferAttr.fragsize = static_cast(-1); + bufferAttr.prebuf = static_cast(-1); + bufferAttr.maxlength = static_cast(-1); + bufferAttr.tlength = static_cast(-1); + bufferAttr.minreq = pa_usec_to_bytes(LATENCY_IN_MSEC * PA_USEC_PER_MSEC, &sampleSpec); + if (eAudioClientType == AUDIO_SERVICE_CLIENT_PLAYBACK) + result = pa_stream_connect_playback(paStream, NULL, &bufferAttr, + (pa_stream_flags_t)(PA_STREAM_ADJUST_LATENCY + | PA_STREAM_INTERPOLATE_TIMING + | PA_STREAM_START_CORKED + | PA_STREAM_AUTO_TIMING_UPDATE), NULL, NULL); + else + result = pa_stream_connect_record(paStream, NULL, NULL, + (pa_stream_flags_t)(PA_STREAM_INTERPOLATE_TIMING + | PA_STREAM_ADJUST_LATENCY + | PA_STREAM_START_CORKED + | PA_STREAM_AUTO_TIMING_UPDATE)); + + if (result < 0) { + error = pa_context_errno(context); + MEDIA_ERR_LOG("error in connection to stream"); + pa_threaded_mainloop_unlock(mainLoop); + ResetPAAudioClient(); + return AUDIO_CLIENT_CREATE_STREAM_ERR; + } + + while (true) { + pa_stream_state_t state = pa_stream_get_state(paStream); + if (state == PA_STREAM_READY) + break; + + if (!PA_STREAM_IS_GOOD(state)) { + error = pa_context_errno(context); + pa_threaded_mainloop_unlock(mainLoop); + MEDIA_ERR_LOG("error in connection to stream"); + ResetPAAudioClient(); + return AUDIO_CLIENT_CREATE_STREAM_ERR; + } + + pa_threaded_mainloop_wait(mainLoop); + } + + isStreamConnected = true; + pa_threaded_mainloop_unlock(mainLoop); + return AUDIO_CLIENT_SUCCESS; +} + +int32_t AudioServiceClient::InitializeAudioCache() +{ + MEDIA_INFO_LOG("Initializing internal audio cache"); + CHECK_PA_STATUS_RET_IF_FAIL(mainLoop, context, paStream, AUDIO_CLIENT_PA_ERR); + + const pa_buffer_attr *bufferAttr = pa_stream_get_buffer_attr(paStream); + if (bufferAttr == NULL) { + MEDIA_ERR_LOG("pa stream get buffer attribute returned null"); + return AUDIO_CLIENT_INIT_ERR; + } + + acache.buffer = make_unique(bufferAttr->minreq); + if (acache.buffer == NULL) { + MEDIA_ERR_LOG("Allocate memory for buffer failed"); + return AUDIO_CLIENT_INIT_ERR; + } + + acache.readIndex = 0; + acache.writeIndex = 0; + acache.totalCacheSize = bufferAttr->minreq; + acache.isFull = false; + return AUDIO_CLIENT_SUCCESS; +} + +int32_t AudioServiceClient::CreateStream(AudioStreamParams audioParams, AudioStreamType audioType) +{ + int error; + + CHECK_AND_RETURN_IFINVALID(mainLoop && context); + + if (eAudioClientType == AUDIO_SERVICE_CLIENT_CONTROLLER) { + return AUDIO_CLIENT_INVALID_PARAMS_ERR; + } + + pa_threaded_mainloop_lock(mainLoop); + const std::string streamName = GetStreamName(audioType); + + sampleSpec = ConvertToPAAudioParams(audioParams); + + pa_proplist *propList = pa_proplist_new(); + if (propList == NULL) { + MEDIA_ERR_LOG("pa_proplist_new failed"); + ResetPAAudioClient(); + return AUDIO_CLIENT_CREATE_STREAM_ERR; + } + + pa_proplist_sets(propList, "stream.type", streamName.c_str()); + + if (!(paStream = pa_stream_new_with_proplist(context, streamName.c_str(), &sampleSpec, NULL, propList))) { + error = pa_context_errno(context); + pa_proplist_free(propList); + pa_threaded_mainloop_unlock(mainLoop); + ResetPAAudioClient(); + return AUDIO_CLIENT_CREATE_STREAM_ERR; + } + + pa_proplist_free(propList); + pa_stream_set_state_callback(paStream, PAStreamStateCb, (void *) this); + pa_stream_set_write_callback(paStream, PAStreamRequestCb, mainLoop); + pa_stream_set_read_callback(paStream, PAStreamRequestCb, mainLoop); + pa_stream_set_latency_update_callback(paStream, PAStreamLatencyUpdateCb, mainLoop); + pa_stream_set_underflow_callback(paStream, PAStreamUnderFlowCb, (void *) this); + + pa_threaded_mainloop_unlock(mainLoop); + + error = ConnectStreamToPA(); + if (error < 0) { + MEDIA_ERR_LOG("Create Stream Failed"); + ResetPAAudioClient(); + return AUDIO_CLIENT_CREATE_STREAM_ERR; + } + + if (eAudioClientType == AUDIO_SERVICE_CLIENT_PLAYBACK) { + error = InitializeAudioCache(); + if (error < 0) { + MEDIA_ERR_LOG("Initialize audio cache failed"); + ResetPAAudioClient(); + return AUDIO_CLIENT_CREATE_STREAM_ERR; + } + } + + MEDIA_INFO_LOG("Created Stream"); + return AUDIO_CLIENT_SUCCESS; +} + +int32_t AudioServiceClient::StartStream() +{ + int error; + + CHECK_PA_STATUS_RET_IF_FAIL(mainLoop, context, paStream, AUDIO_CLIENT_PA_ERR); + pa_operation *operation = NULL; + + pa_threaded_mainloop_lock(mainLoop); + + pa_stream_state_t state = pa_stream_get_state(paStream); + if (state != PA_STREAM_READY) { + error = pa_context_errno(context); + pa_threaded_mainloop_unlock(mainLoop); + MEDIA_ERR_LOG("Stream Start Failed"); + ResetPAAudioClient(); + return AUDIO_CLIENT_START_STREAM_ERR; + } + + streamCmdStatus = 0; + operation = pa_stream_cork(paStream, 0, PAStreamCmdSuccessCb, (void *) this); + + while (pa_operation_get_state(operation) == PA_OPERATION_RUNNING) { + pa_threaded_mainloop_wait(mainLoop); + } + pa_operation_unref(operation); + pa_threaded_mainloop_unlock(mainLoop); + + if (!streamCmdStatus) { + MEDIA_ERR_LOG("Stream Start Failed"); + ResetPAAudioClient(); + return AUDIO_CLIENT_START_STREAM_ERR; + } else { + MEDIA_INFO_LOG("Stream Started Successfully"); + return AUDIO_CLIENT_SUCCESS; + } +} + +int32_t AudioServiceClient::PauseStream() +{ + return StopStream(); +} + +int32_t AudioServiceClient::StopStream() +{ + CHECK_PA_STATUS_RET_IF_FAIL(mainLoop, context, paStream, AUDIO_CLIENT_PA_ERR); + pa_operation *operation = NULL; + + pa_threaded_mainloop_lock(mainLoop); + pa_stream_state_t state = pa_stream_get_state(paStream); + if (state != PA_STREAM_READY) { + int32_t error = pa_context_errno(context); + pa_threaded_mainloop_unlock(mainLoop); + MEDIA_ERR_LOG("Stream Stop Failed : %{public}d", error); + return AUDIO_CLIENT_ERR; + } + + streamCmdStatus = 0; + operation = pa_stream_cork(paStream, 1, PAStreamCmdSuccessCb, (void *) this); + + while (pa_operation_get_state(operation) == PA_OPERATION_RUNNING) { + pa_threaded_mainloop_wait(mainLoop); + } + pa_operation_unref(operation); + pa_threaded_mainloop_unlock(mainLoop); + + if (!streamCmdStatus) { + MEDIA_ERR_LOG("Stream Stop Failed"); + return AUDIO_CLIENT_ERR; + } else { + MEDIA_INFO_LOG("Stream Stopped Successfully"); + return AUDIO_CLIENT_SUCCESS; + } +} + +int32_t AudioServiceClient::FlushStream() +{ + int error; + CHECK_PA_STATUS_RET_IF_FAIL(mainLoop, context, paStream, AUDIO_CLIENT_PA_ERR); + pa_operation *operation = NULL; + + pa_threaded_mainloop_lock(mainLoop); + + pa_stream_state_t state = pa_stream_get_state(paStream); + if (state != PA_STREAM_READY) { + error = pa_context_errno(context); + pa_threaded_mainloop_unlock(mainLoop); + MEDIA_ERR_LOG("Stream Flush Failed"); + return AUDIO_CLIENT_ERR; + } + + streamCmdStatus = 0; + operation = pa_stream_flush(paStream, PAStreamCmdSuccessCb, (void *) this); + + while (pa_operation_get_state(operation) == PA_OPERATION_RUNNING) { + pa_threaded_mainloop_wait(mainLoop); + } + pa_operation_unref(operation); + pa_threaded_mainloop_unlock(mainLoop); + + if (!streamCmdStatus) { + MEDIA_ERR_LOG("Stream Flush Failed"); + return AUDIO_CLIENT_ERR; + } else { + MEDIA_INFO_LOG("Stream Flushed Successfully"); + acache.readIndex = 0; + acache.writeIndex = 0; + acache.isFull = false; + return AUDIO_CLIENT_SUCCESS; + } +} + +int32_t AudioServiceClient::DrainStream() +{ + int error; + + if (eAudioClientType != AUDIO_SERVICE_CLIENT_PLAYBACK) { + MEDIA_ERR_LOG("Drain is not supported"); + return AUDIO_CLIENT_ERR; + } + + error = DrainAudioCache(); + if (error != AUDIO_CLIENT_SUCCESS) { + MEDIA_ERR_LOG("Audio cache drain failed"); + return AUDIO_CLIENT_ERR; + } + + CHECK_PA_STATUS_RET_IF_FAIL(mainLoop, context, paStream, AUDIO_CLIENT_PA_ERR); + pa_operation *operation = NULL; + + pa_threaded_mainloop_lock(mainLoop); + + pa_stream_state_t state = pa_stream_get_state(paStream); + if (state != PA_STREAM_READY) { + error = pa_context_errno(context); + pa_threaded_mainloop_unlock(mainLoop); + MEDIA_ERR_LOG("Stream Drain Failed"); + return AUDIO_CLIENT_ERR; + } + + streamCmdStatus = 0; + operation = pa_stream_drain(paStream, PAStreamCmdSuccessCb, (void *) this); + + while (pa_operation_get_state(operation) == PA_OPERATION_RUNNING) { + pa_threaded_mainloop_wait(mainLoop); + } + pa_operation_unref(operation); + pa_threaded_mainloop_unlock(mainLoop); + + if (!streamCmdStatus) { + MEDIA_ERR_LOG("Stream Drain Failed"); + return AUDIO_CLIENT_ERR; + } else { + MEDIA_INFO_LOG("Stream Drained Successfully"); + return AUDIO_CLIENT_SUCCESS; + } +} + +int32_t AudioServiceClient::SetStreamVolume(uint32_t sessionID, uint32_t volume) +{ + return AUDIO_CLIENT_SUCCESS; +} + +int32_t AudioServiceClient::PaWriteStream(const uint8_t *buffer, size_t &length) +{ + int error = 0; + + while (length > 0) { + size_t writableSize; + + while (!(writableSize = pa_stream_writable_size(paStream))) { + pa_threaded_mainloop_wait(mainLoop); + } + + MEDIA_INFO_LOG("Write stream: writable size = %{public}zu, length = %{public}zu", + writableSize, length); + if (writableSize > length) { + writableSize = length; + } + + writableSize = AlignToAudioFrameSize(writableSize, sampleSpec); + if (writableSize == 0) { + MEDIA_ERR_LOG("Align to frame size failed"); + error = AUDIO_CLIENT_WRITE_STREAM_ERR; + break; + } + + error = pa_stream_write(paStream, (void *)buffer, writableSize, NULL, 0LL, + PA_SEEK_RELATIVE); + if (error < 0) { + MEDIA_ERR_LOG("Write stream failed"); + error = AUDIO_CLIENT_WRITE_STREAM_ERR; + break; + } + + MEDIA_INFO_LOG("Writable size: %{public}zu, bytes to write: %{public}zu, return val: %{public}d", + writableSize, length, error); + buffer = buffer + writableSize; + length -= writableSize; + acache.readIndex += writableSize; + acache.isFull = false; + } + + return error; +} + +int32_t AudioServiceClient::DrainAudioCache() +{ + CHECK_PA_STATUS_RET_IF_FAIL(mainLoop, context, paStream, AUDIO_CLIENT_PA_ERR); + pa_threaded_mainloop_lock(mainLoop); + + int32_t error = 0; + if (acache.buffer == NULL) { + MEDIA_ERR_LOG("Drain cache failed"); + return AUDIO_CLIENT_ERR; + } + + size_t length = acache.writeIndex - acache.readIndex; + const uint8_t *buffer = acache.buffer.get(); + + error = PaWriteStream(buffer, length); + + acache.readIndex = 0; + acache.writeIndex = 0; + + pa_threaded_mainloop_unlock(mainLoop); + return error; +} + +size_t AudioServiceClient::WriteToAudioCache(const StreamBuffer &stream) +{ + if (stream.buffer == NULL) { + return 0; + } + + const uint8_t *inputBuffer = stream.buffer; + uint8_t *cacheBuffer = acache.buffer.get() + acache.writeIndex; + + size_t inputLen = stream.bufferLen; + + while (inputLen > 0) { + size_t writableSize = acache.totalCacheSize - acache.writeIndex; + + if (writableSize > inputLen) { + writableSize = inputLen; + } + + if (writableSize == 0) { + break; + } + + if (memcpy_s(cacheBuffer, acache.totalCacheSize, inputBuffer, writableSize)) { + break; + } + + inputBuffer = inputBuffer + writableSize; + cacheBuffer = cacheBuffer + writableSize; + inputLen -= writableSize; + acache.writeIndex += writableSize; + } + + if ((acache.writeIndex - acache.readIndex) == acache.totalCacheSize) { + acache.isFull = true; + } + + return (stream.bufferLen - inputLen); +} + +size_t AudioServiceClient::WriteStream(const StreamBuffer &stream, int32_t &pError) +{ + lock_guard lock(mtx); + int error = 0; + size_t cachedLen = WriteToAudioCache(stream); + + if (!acache.isFull) { + pError = error; + return cachedLen; + } + + CHECK_PA_STATUS_FOR_WRITE(mainLoop, context, paStream, pError, 0); + pa_threaded_mainloop_lock(mainLoop); + + if (acache.buffer == NULL) { + MEDIA_ERR_LOG("Buffer is null"); + pError = AUDIO_CLIENT_WRITE_STREAM_ERR; + return cachedLen; + } + + const uint8_t *buffer = acache.buffer.get(); + size_t length = acache.totalCacheSize; + + error = PaWriteStream(buffer, length); + + if (!error && (length >= 0) && !acache.isFull) { + uint8_t *cacheBuffer = acache.buffer.get(); + uint32_t offset = acache.readIndex; + uint32_t size = (acache.writeIndex - acache.readIndex); + if (size > 0) { + if (memcpy_s(cacheBuffer, acache.totalCacheSize, cacheBuffer + offset, size)) { + MEDIA_ERR_LOG("Update cache failed"); + pError = AUDIO_CLIENT_WRITE_STREAM_ERR; + return cachedLen; + } + MEDIA_INFO_LOG("rearranging the audio cache"); + } + acache.readIndex = 0; + acache.writeIndex = 0; + + if (cachedLen < stream.bufferLen) { + StreamBuffer str; + str.buffer = stream.buffer + cachedLen; + str.bufferLen = stream.bufferLen - cachedLen; + MEDIA_INFO_LOG("writing pending data to audio cache: %{public}d", str.bufferLen); + cachedLen += WriteToAudioCache(str); + } + } + + pa_threaded_mainloop_unlock(mainLoop); + pError = error; + return cachedLen; +} + +int32_t AudioServiceClient::UpdateReadBuffer(uint8_t *buffer, size_t &length, size_t &readSize) +{ + size_t l = (internalRdBufLen < length) ? internalRdBufLen : length; + memcpy_s(buffer, length, (const uint8_t*) internalReadBuffer + internalRdBufIndex, l); + + length -= l; + internalRdBufIndex += l; + internalRdBufLen -= l; + readSize += l; + + if (!internalRdBufLen) { + int retVal = pa_stream_drop(paStream); + internalReadBuffer = NULL; + internalRdBufLen = 0; + internalRdBufIndex = 0; + if (retVal < 0) { + MEDIA_ERR_LOG("pa_stream_drop failed, retVal: %d", retVal); + return AUDIO_CLIENT_READ_STREAM_ERR; + } + } + + return 0; +} + +int32_t AudioServiceClient::ReadStream(StreamBuffer &stream, bool isBlocking) +{ + uint8_t* buffer = stream.buffer; + size_t length = stream.bufferLen; + size_t readSize = 0; + + CHECK_PA_STATUS_RET_IF_FAIL(mainLoop, context, paStream, AUDIO_CLIENT_PA_ERR); + + pa_threaded_mainloop_lock(mainLoop); + while (length > 0) { + while (!internalReadBuffer) { + int retVal = pa_stream_peek(paStream, &internalReadBuffer, &internalRdBufLen); + if (retVal < 0) { + MEDIA_ERR_LOG("pa_stream_peek failed, retVal: %d", retVal); + pa_threaded_mainloop_unlock(mainLoop); + return AUDIO_CLIENT_READ_STREAM_ERR; + } + + if (internalRdBufLen <= 0) { + if (isBlocking) + pa_threaded_mainloop_wait(mainLoop); + else { + pa_threaded_mainloop_unlock(mainLoop); + return 0; + } + } else if (!internalReadBuffer) { + retVal = pa_stream_drop(paStream); + if (retVal < 0) { + MEDIA_ERR_LOG("pa_stream_drop failed, retVal: %d", retVal); + pa_threaded_mainloop_unlock(mainLoop); + return AUDIO_CLIENT_READ_STREAM_ERR; + } + } else { + internalRdBufIndex = 0; + MEDIA_INFO_LOG("buffer size from PA: %zu", internalRdBufLen); + } + } + + if (UpdateReadBuffer(buffer, length, readSize) != 0) { + pa_threaded_mainloop_unlock(mainLoop); + return AUDIO_CLIENT_READ_STREAM_ERR; + } + buffer = stream.buffer + readSize; + } + pa_threaded_mainloop_unlock(mainLoop); + return readSize; +} + +int32_t AudioServiceClient::ReleaseStream() +{ + ResetPAAudioClient(); + return AUDIO_CLIENT_SUCCESS; +} + +int32_t AudioServiceClient::GetMinimumBufferSize(size_t &minBufferSize) +{ + CHECK_PA_STATUS_RET_IF_FAIL(mainLoop, context, paStream, AUDIO_CLIENT_PA_ERR); + + const pa_buffer_attr* bufferAttr = pa_stream_get_buffer_attr(paStream); + + if (bufferAttr == NULL) { + MEDIA_ERR_LOG("pa_stream_get_buffer_attr returned NULL"); + return AUDIO_CLIENT_ERR; + } + + if (eAudioClientType == AUDIO_SERVICE_CLIENT_PLAYBACK) { + minBufferSize = (size_t) MINIMUM_BUFFER_SIZE; + } + + if (eAudioClientType == AUDIO_SERVICE_CLIENT_RECORD) { + minBufferSize = (size_t) bufferAttr->fragsize; + } + + MEDIA_INFO_LOG("buffer size: %zu", minBufferSize); + return AUDIO_CLIENT_SUCCESS; +} + +int32_t AudioServiceClient::GetMinimumFrameCount(uint32_t &frameCount) +{ + CHECK_PA_STATUS_RET_IF_FAIL(mainLoop, context, paStream, AUDIO_CLIENT_PA_ERR); + size_t minBufferSize = 0; + + const pa_buffer_attr* bufferAttr = pa_stream_get_buffer_attr(paStream); + + if (bufferAttr == NULL) { + MEDIA_ERR_LOG("pa_stream_get_buffer_attr returned NULL"); + return AUDIO_CLIENT_ERR; + } + + if (eAudioClientType == AUDIO_SERVICE_CLIENT_PLAYBACK) { + minBufferSize = (size_t) MINIMUM_BUFFER_SIZE; + } + + if (eAudioClientType == AUDIO_SERVICE_CLIENT_RECORD) { + minBufferSize = (size_t) bufferAttr->fragsize; + } + + uint32_t bytesPerSample = pa_frame_size(&sampleSpec); + if (bytesPerSample == 0) { + MEDIA_ERR_LOG("GetMinimumFrameCount Failed"); + return AUDIO_CLIENT_ERR; + } + + frameCount = minBufferSize / bytesPerSample; + MEDIA_INFO_LOG("frame count: %d", frameCount); + return AUDIO_CLIENT_SUCCESS; +} + +uint32_t AudioServiceClient::GetSamplingRate() +{ + return DEFAULT_SAMPLING_RATE; +} + +uint8_t AudioServiceClient::GetChannelCount() +{ + return DEFAULT_CHANNEL_COUNT; +} + +uint8_t AudioServiceClient::GetSampleSize() +{ + return DEFAULT_SAMPLE_SIZE; +} + +int32_t AudioServiceClient::GetAudioStreamParams(AudioStreamParams& audioParams) +{ + CHECK_PA_STATUS_RET_IF_FAIL(mainLoop, context, paStream, AUDIO_CLIENT_PA_ERR); + const pa_sample_spec* paSampleSpec = pa_stream_get_sample_spec(paStream); + + audioParams = ConvertFromPAAudioParams(*paSampleSpec); + return AUDIO_CLIENT_SUCCESS; +} + +uint32_t AudioServiceClient::GetStreamVolume(uint32_t sessionID) +{ + return DEFAULT_STREAM_VOLUME; +} + +int32_t AudioServiceClient::GetCurrentTimeStamp(uint64_t &timeStamp) +{ + CHECK_PA_STATUS_RET_IF_FAIL(mainLoop, context, paStream, AUDIO_CLIENT_PA_ERR); + int32_t retVal = AUDIO_CLIENT_SUCCESS; + + pa_threaded_mainloop_lock(mainLoop); + const pa_timing_info *info = pa_stream_get_timing_info(paStream); + if (!info) { + retVal = AUDIO_CLIENT_ERR; + } else { + if (eAudioClientType == AUDIO_SERVICE_CLIENT_PLAYBACK) { + timeStamp = pa_bytes_to_usec(info->write_index, &sampleSpec); + } else if (eAudioClientType == AUDIO_SERVICE_CLIENT_RECORD) { + timeStamp = pa_bytes_to_usec(info->read_index, &sampleSpec); + } + } + pa_threaded_mainloop_unlock(mainLoop); + + return retVal; +} + +int32_t AudioServiceClient::GetAudioLatency(uint64_t &latency) +{ + CHECK_PA_STATUS_RET_IF_FAIL(mainLoop, context, paStream, AUDIO_CLIENT_PA_ERR); + pa_usec_t paLatency; + pa_usec_t cacheLatency; + int32_t retVal = AUDIO_CLIENT_SUCCESS; + int negative; + bool getPALatency = false; + + // Get PA latency + pa_threaded_mainloop_lock(mainLoop); + while (!getPALatency) { + if (pa_stream_get_latency(paStream, &paLatency, &negative) >= 0) { + if (negative) { + latency = 0; + retVal = AUDIO_CLIENT_ERR; + return retVal; + } + getPALatency = true; + break; + } + MEDIA_INFO_LOG("waiting for audio latency information"); + pa_threaded_mainloop_wait(mainLoop); + } + pa_threaded_mainloop_unlock(mainLoop); + + if (eAudioClientType == AUDIO_SERVICE_CLIENT_PLAYBACK) { + // Get audio write cache latency + cacheLatency = pa_bytes_to_usec((acache.totalCacheSize - acache.writeIndex), &sampleSpec); + + // Total latency will be sum of audio write cache latency + PA latency + latency = paLatency + cacheLatency; + MEDIA_INFO_LOG("total latency: %{public}llu, pa latency: %{public}llu, cache latency: %{public}llu", latency, paLatency, cacheLatency); + } else if (eAudioClientType == AUDIO_SERVICE_CLIENT_RECORD) { + // Get audio read cache latency + cacheLatency = pa_bytes_to_usec(internalRdBufLen, &sampleSpec); + + // Total latency will be sum of audio read cache latency + PA latency + latency = paLatency + cacheLatency; + MEDIA_INFO_LOG("total latency: %{public}llu, pa latency: %{public}llu", latency, paLatency); + } + + return retVal; +} + +void AudioServiceClient::RegisterAudioRendererCallbacks(const AudioRendererCallbacks &cb) +{ + MEDIA_INFO_LOG("Registering audio render callbacks"); + mAudioRendererCallbacks = (AudioRendererCallbacks *) &cb; +} + +void AudioServiceClient::RegisterAudioRecorderCallbacks(const AudioRecorderCallbacks &cb) +{ + MEDIA_INFO_LOG("Registering audio record callbacks"); + mAudioRecorderCallbacks = (AudioRecorderCallbacks *) &cb; +} +} // namespace AudioStandard +} // namespace OHOS diff --git a/services/src/client/audio_session.cpp b/services/src/client/audio_session.cpp new file mode 100644 index 0000000000000000000000000000000000000000..abc4fa75f83fd46319d157eec9d8582e27d458c8 --- /dev/null +++ b/services/src/client/audio_session.cpp @@ -0,0 +1,16 @@ +/* + * Copyright (C) 2021 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_session.h" diff --git a/services/src/client/audio_stream.cpp b/services/src/client/audio_stream.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7eb856689e3c105688f581b432e44e240b757227 --- /dev/null +++ b/services/src/client/audio_stream.cpp @@ -0,0 +1,378 @@ +/* + * Copyright (C) 2021 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_stream.h" +#include +#include "audio_errors.h" +#include "audio_info.h" +#include "media_log.h" + +using namespace std; + +namespace OHOS { +namespace AudioStandard { +const unsigned long long TIME_CONVERSION_US_S = 1000000ULL; /* us to s */ +const unsigned long long TIME_CONVERSION_NS_US = 1000ULL; /* ns to us */ + +AudioStream::AudioStream(AudioStreamType eStreamType, AudioMode eMode) : eStreamType_(eStreamType), + eMode_(eMode), + state_(NEW), + isReadInProgress_(false), + isWriteInProgress_(false) +{ + MEDIA_DEBUG_LOG("AudioStream ctor"); +} + +AudioStream::~AudioStream() +{ + if (state_ != RELEASED) { + ReleaseAudioStream(); + } +} + +State AudioStream::GetState() +{ + return state_; +} + +bool AudioStream::GetAudioTime(Timestamp ×tamp, Timestamp::Timestampbase base) +{ + uint64_t paTimeStamp; + if (GetCurrentTimeStamp(paTimeStamp) == SUCCESS) { + MEDIA_DEBUG_LOG("AudioStream: GetAudioTime in microseconds: %{public}lld", paTimeStamp); + timestamp.time.tv_sec = static_cast(paTimeStamp / TIME_CONVERSION_US_S); + timestamp.time.tv_nsec + = static_cast((paTimeStamp - (timestamp.time.tv_sec * TIME_CONVERSION_US_S)) + * TIME_CONVERSION_NS_US); + return true; + } + return false; +} + +int32_t AudioStream::GetBufferSize(size_t &bufferSize) +{ + MEDIA_INFO_LOG("AudioStream: Get Buffer size"); + if (GetMinimumBufferSize(bufferSize) != 0) { + return ERR_OPERATION_FAILED; + } + + return SUCCESS; +} + +int32_t AudioStream::GetFrameCount(uint32_t &frameCount) +{ + MEDIA_INFO_LOG("AudioStream: Get frame count"); + if (GetMinimumFrameCount(frameCount) != 0) { + return ERR_OPERATION_FAILED; + } + + return SUCCESS; +} + +int32_t AudioStream::GetLatency(uint64_t &latency) +{ + if (GetAudioLatency(latency) != SUCCESS) { + return ERR_OPERATION_FAILED; + } else { + return SUCCESS; + } +} + +vector AudioStream::GetSupportedFormats() +{ + return AUDIO_SUPPORTED_FORMATS; +} + +vector AudioStream::GetSupportedChannels() +{ + return AUDIO_SUPPORTED_CHANNELS; +} + +vector AudioStream::GetSupportedEncodingTypes() +{ + return AUDIO_SUPPORTED_ENCODING_TYPES; +} + +vector AudioStream::GetSupportedSamplingRates() +{ + 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()); + MEDIA_DEBUG_LOG("AudioStream: IsFormatValid: %{public}s", isValidFormat ? "true" : "false"); + return isValidFormat; +} + +bool IsChannelValid(uint8_t channel) +{ + bool isValidChannel = (find(AUDIO_SUPPORTED_CHANNELS.begin(), AUDIO_SUPPORTED_CHANNELS.end(), channel) + != AUDIO_SUPPORTED_CHANNELS.end()); + MEDIA_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()); + MEDIA_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()); + MEDIA_DEBUG_LOG("AudioStream: IsSamplingRateValid: %{public}s", + isValidSamplingRate ? "true" : "false"); + return isValidSamplingRate; +} + +int32_t AudioStream::GetAudioStreamInfo(AudioStreamParams &audioStreamInfo) +{ + MEDIA_INFO_LOG("AudioStream: GetAudioStreamInfo"); + if (GetAudioStreamParams(audioStreamInfo) != 0) { + return ERR_OPERATION_FAILED; + } + + return SUCCESS; +} + +int32_t AudioStream::SetAudioStreamInfo(const AudioStreamParams info) +{ + MEDIA_INFO_LOG("AudioStream: SetAudioParams"); + + MEDIA_DEBUG_LOG("AudioStream: Sampling rate: %{public}d", info.samplingRate); + MEDIA_DEBUG_LOG("AudioStream: channels: %{public}d", info.channels); + MEDIA_DEBUG_LOG("AudioStream: format: %{public}d", info.format); + MEDIA_DEBUG_LOG("AudioStream: stream type: %{public}d", eStreamType_); + + if (!IsFormatValid(info.format) || !IsChannelValid(info.channels) + || !IsSamplingRateValid(info.samplingRate) || !IsEncodingTypeValid(info.encoding)) { + MEDIA_ERR_LOG("AudioStream: Unsupported audio parameter"); + return ERR_NOT_SUPPORTED; + } + if (state_ != NEW) { + MEDIA_DEBUG_LOG("AudioStream: State is not new, release existing stream"); + StopAudioStream(); + ReleaseAudioStream(); + } + + int32_t ret = 0; + switch (eMode_) { + case AUDIO_MODE_PLAYBACK: + MEDIA_DEBUG_LOG("AudioStream: Initialize playback"); + ret = Initialize(AUDIO_SERVICE_CLIENT_PLAYBACK); + break; + case AUDIO_MODE_RECORD: + MEDIA_DEBUG_LOG("AudioStream: Initialize recorder"); + ret = Initialize(AUDIO_SERVICE_CLIENT_RECORD); + break; + default: + return ERR_INVALID_OPERATION; + } + + if (ret) { + MEDIA_DEBUG_LOG("AudioStream: Error initializing!"); + return ret; + } + + if (CreateStream(info, eStreamType_) != SUCCESS) { + MEDIA_ERR_LOG("AudioStream:Create stream failed"); + return ERROR; + } + + state_ = PREPARED; + MEDIA_INFO_LOG("AudioStream:Set stream Info SUCCESS"); + return SUCCESS; +} + +bool AudioStream::StartAudioStream() +{ + if ((state_ != PREPARED) && (state_ != STOPPED) && (state_ != PAUSED)) { + MEDIA_ERR_LOG("StartAudioStream Illegal state:%{public}u", state_); + return false; + } + int32_t ret = StartStream(); + if (ret != SUCCESS) { + MEDIA_ERR_LOG("StartStream Start failed:0x%{public}x", ret); + return false; + } + + state_ = RUNNING; + MEDIA_INFO_LOG("StartAudioStream SUCCESS"); + return true; +} + +int32_t AudioStream::Read(uint8_t &buffer, size_t userSize, bool isBlockingRead) +{ + if (userSize <= 0) { + MEDIA_ERR_LOG("Invalid userSize:%{public}zu", userSize); + return ERR_INVALID_PARAM; + } + + if (state_ != RUNNING) { + MEDIA_ERR_LOG("Read: State is not RUNNNIG. Illegal state:%{public}u", state_); + return ERR_ILLEGAL_STATE; + } + + StreamBuffer stream; + stream.buffer = &buffer; + stream.bufferLen = userSize; + isReadInProgress_ = true; + int32_t readLen = ReadStream(stream, isBlockingRead); + isReadInProgress_ = false; + if (readLen < 0) { + MEDIA_ERR_LOG("ReadStream fail,ret:0x%{public}x", readLen); + return ERR_INVALID_READ; + } + + return readLen; +} + +size_t AudioStream::Write(uint8_t *buffer, size_t buffer_size) +{ + if ((buffer == nullptr) || (buffer_size <= 0)) { + MEDIA_ERR_LOG("Invalid buffer size:%{public}zu", buffer_size); + return ERR_INVALID_PARAM; + } + + if (state_ != RUNNING) { + MEDIA_ERR_LOG("Write: Illegal state:%{public}u", state_); + return ERR_ILLEGAL_STATE; + } + + int32_t writeError; + StreamBuffer stream; + stream.buffer = buffer; + stream.bufferLen = buffer_size; + isWriteInProgress_ = true; + size_t bytesWritten = WriteStream(stream, writeError); + isWriteInProgress_ = false; + if (writeError != 0) { + MEDIA_ERR_LOG("WriteStream fail,writeError:%{public}d", writeError); + return ERR_WRITE_FAILED; + } + if (bytesWritten < 0) { + MEDIA_ERR_LOG("WriteStream fail,bytesWritten:0x%{public}x", bytesWritten); + return ERR_INVALID_WRITE; + } + + return bytesWritten; +} + +bool AudioStream::PauseAudioStream() +{ + if (state_ != RUNNING) { + MEDIA_ERR_LOG("PauseAudioStream: State is not RUNNING. Illegal state:%{public}u", state_); + return false; + } + State oldState = state_; + state_ = PAUSED; // Set it before stopping as Read/Write and Stop can be called from different threads + while (isReadInProgress_ || isWriteInProgress_) { + } + + int32_t ret = PauseStream(); + if (ret != SUCCESS) { + MEDIA_DEBUG_LOG("StreamPause fail,ret:0x%{public}x", ret); + state_ = oldState; + return false; + } + MEDIA_INFO_LOG("PauseAudioStream SUCCESS"); + + return true; +} + +bool AudioStream::StopAudioStream() +{ + if (state_ != RUNNING) { + MEDIA_ERR_LOG("StopAudioStream: State is not RUNNING. Illegal state:%{public}u", state_); + return false; + } + State oldState = state_; + state_ = STOPPED; // Set it before stopping as Read/Write and Stop can be called from different threads + while (isReadInProgress_ || isWriteInProgress_) { + } + + int32_t ret = StopStream(); + if (ret != SUCCESS) { + MEDIA_DEBUG_LOG("StreamStop fail,ret:0x%{public}x", ret); + state_ = oldState; + return false; + } + MEDIA_INFO_LOG("StopAudioStream SUCCESS"); + + return true; +} + +bool AudioStream::FlushAudioStream() +{ + if ((state_ != RUNNING) && (state_ != PAUSED)) { + MEDIA_ERR_LOG("FlushAudioStream: State is not RUNNING. Illegal state:%{public}u", state_); + return false; + } + + int32_t ret = FlushStream(); + if (ret != SUCCESS) { + MEDIA_DEBUG_LOG("Flush stream fail,ret:0x%{public}x", ret); + return false; + } + + MEDIA_INFO_LOG("Flush stream SUCCESS"); + return true; +} + +bool AudioStream::DrainAudioStream() +{ + if (state_ != RUNNING) { + MEDIA_ERR_LOG("DrainAudioStream: State is not RUNNING. Illegal state:%{public}u", state_); + return false; + } + + int32_t ret = DrainStream(); + if (ret != SUCCESS) { + MEDIA_DEBUG_LOG("Drain stream fail,ret:0x%{public}x", ret); + return false; + } + + MEDIA_INFO_LOG("Drain stream SUCCESS"); + return true; +} + +bool AudioStream::ReleaseAudioStream() +{ + if (state_ == RELEASED) { + MEDIA_ERR_LOG("Already Released. Illegal state: state = %{public}u", state_); + return false; + } + // If state_ is RUNNING try to Stop it first and Release + if (state_ == RUNNING) { + StopAudioStream(); + } + + ReleaseStream(); + state_ = RELEASED; + MEDIA_INFO_LOG("Release Audio stream SUCCESS"); + + return true; +} +} +} diff --git a/services/src/client/audio_system_manager.cpp b/services/src/client/audio_system_manager.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ffe8bfd4d7032ef07b716d839a9bd087cc1f327f --- /dev/null +++ b/services/src/client/audio_system_manager.cpp @@ -0,0 +1,272 @@ +/* + * Copyright (C) 2021 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_errors.h" +#include "audio_manager_proxy.h" +#include "audio_policy_manager.h" +#include "audio_stream.h" +#include "audio_system_manager.h" +#include "iservice_registry.h" +#include "media_log.h" +#include "system_ability_definition.h" + +namespace OHOS { +namespace AudioStandard { +using namespace std; +static sptr g_sProxy = nullptr; + +AudioSystemManager::AudioSystemManager() +{ + MEDIA_DEBUG_LOG("AudioSystemManager start"); + init(); +} + +AudioSystemManager::~AudioSystemManager() +{ + MEDIA_DEBUG_LOG("AudioSystemManager::~AudioSystemManager"); +} + +AudioSystemManager* AudioSystemManager::GetInstance() +{ + static AudioSystemManager audioManager; + return &audioManager; +} + +void AudioSystemManager::init() +{ + auto samgr = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager(); + if (samgr == nullptr) { + MEDIA_ERR_LOG("AudioSystemManager::init failed"); + return; + } + + sptr object = samgr->GetSystemAbility(AUDIO_DISTRIBUTED_SERVICE_ID); + if (object == nullptr) { + MEDIA_DEBUG_LOG("AudioSystemManager::object is NULL."); + } + g_sProxy = iface_cast(object); + if (g_sProxy == nullptr) { + MEDIA_DEBUG_LOG("AudioSystemManager::init g_sProxy is NULL."); + } else { + MEDIA_DEBUG_LOG("AudioSystemManager::init g_sProxy is assigned."); + } +} + +bool AudioSystemManager::SetRingerMode(AudioRingerMode ringMode) const +{ + /* Call Audio Policy SetRingerMode */ + return AudioPolicyManager::GetInstance().SetRingerMode(ringMode); +} + +AudioRingerMode AudioSystemManager::GetRingerMode() const +{ + /* Call Audio Policy GetRingerMode */ + return (AudioPolicyManager::GetInstance().GetRingerMode()); +} + +int32_t AudioSystemManager::SetDeviceActive(AudioDeviceDescriptor::DeviceType deviceType, bool flag) const +{ + switch (deviceType) { + case SPEAKER: + case BLUETOOTH_A2DP: + case MIC: + case BLUETOOTH_SCO: + break; + default: + MEDIA_ERR_LOG("SetDeviceActive device=%{public}d not supported", deviceType); + return ERR_NOT_SUPPORTED; + } + + /* Call Audio Policy SetDeviceActive */ + DeviceType devType = (DeviceType)deviceType; + return (AudioPolicyManager::GetInstance().SetDeviceActive(devType, flag)); +} + +bool AudioSystemManager::IsDeviceActive(AudioDeviceDescriptor::DeviceType deviceType) const +{ + switch (deviceType) { + case SPEAKER: + case BLUETOOTH_A2DP: + case MIC: + case BLUETOOTH_SCO: + break; + default: + MEDIA_ERR_LOG("IsDeviceActive device=%{public}d not supported", deviceType); + return false; + } + + /* Call Audio Policy IsDeviceActive */ + DeviceType devType = (DeviceType)deviceType; + return (AudioPolicyManager::GetInstance().IsDeviceActive(devType)); +} + +bool AudioSystemManager::IsStreamActive(AudioSystemManager::AudioVolumeType volumeType) const +{ + switch (volumeType) { + case STREAM_MUSIC: + case STREAM_RING: + break; + default: + MEDIA_ERR_LOG("IsStreamActive volumeType=%{public}d not supported", volumeType); + return false; + } + + AudioStreamType StreamVolType = (AudioStreamType)volumeType; + return AudioPolicyManager::GetInstance().IsStreamActive(StreamVolType); +} + +const std::string AudioSystemManager::GetAudioParameter(const std::string key) const +{ + return g_sProxy->GetAudioParameter(key); +} + +void AudioSystemManager::SetAudioParameter(const std::string key, const std::string value) const +{ + g_sProxy->SetAudioParameter(key, value); +} + +int32_t AudioSystemManager::SetVolume(AudioSystemManager::AudioVolumeType volumeType, float volume) const +{ + /* Validate and return INVALID_PARAMS error */ + if ((volume < 0) || (volume > 1)) { + MEDIA_ERR_LOG("Invalid Volume Input!"); + return ERR_INVALID_PARAM; + } + + switch (volumeType) { + case STREAM_MUSIC: + case STREAM_RING: + break; + default: + MEDIA_ERR_LOG("SetVolume volumeType=%{public}d not supported", volumeType); + return ERR_NOT_SUPPORTED; + } + + /* Call Audio Policy SetStreamVolume */ + AudioStreamType StreamVolType = (AudioStreamType)volumeType; + return AudioPolicyManager::GetInstance().SetStreamVolume(StreamVolType, volume); +} + +float AudioSystemManager::GetVolume(AudioSystemManager::AudioVolumeType volumeType) const +{ + switch (volumeType) { + case STREAM_MUSIC: + case STREAM_RING: + break; + default: + MEDIA_ERR_LOG("GetVolume volumeType=%{public}d not supported", volumeType); + return (float)ERR_NOT_SUPPORTED; + } + + /* Call Audio Policy SetStreamMute */ + AudioStreamType StreamVolType = (AudioStreamType)volumeType; + return AudioPolicyManager::GetInstance().GetStreamVolume(StreamVolType); +} + +float AudioSystemManager::GetMaxVolume(AudioSystemManager::AudioVolumeType volumeType) const +{ + return g_sProxy->GetMaxVolume(volumeType); +} + +float AudioSystemManager::GetMinVolume(AudioSystemManager::AudioVolumeType volumeType) const +{ + return g_sProxy->GetMinVolume(volumeType); +} + +int32_t AudioSystemManager::SetMute(AudioSystemManager::AudioVolumeType volumeType, bool mute) const +{ + switch (volumeType) { + case STREAM_MUSIC: + case STREAM_RING: + break; + default: + MEDIA_ERR_LOG("SetMute volumeType=%{public}d not supported", volumeType); + return ERR_NOT_SUPPORTED; + } + + /* Call Audio Policy SetStreamMute */ + AudioStreamType StreamVolType = (AudioStreamType)volumeType; + return AudioPolicyManager::GetInstance().SetStreamMute(StreamVolType, mute); +} + +bool AudioSystemManager::IsStreamMute(AudioSystemManager::AudioVolumeType volumeType) const +{ + MEDIA_DEBUG_LOG("AudioSystemManager::GetMute Client"); + + switch (volumeType) { + case STREAM_MUSIC: + case STREAM_RING: + break; + default: + MEDIA_ERR_LOG("IsStreamMute volumeType=%{public}d not supported", volumeType); + return false; + } + + /* Call Audio Policy SetStreamVolume */ + AudioStreamType StreamVolType = (AudioStreamType)volumeType; + return AudioPolicyManager::GetInstance().GetStreamMute(StreamVolType); +} + +int32_t AudioSystemManager::SetMicrophoneMute(bool isMute) const +{ + return g_sProxy->SetMicrophoneMute(isMute); +} + +bool AudioSystemManager::IsMicrophoneMute() const +{ + return g_sProxy->IsMicrophoneMute(); +} + +std::vector> AudioSystemManager::GetDevices(AudioDeviceDescriptor::DeviceFlag deviceFlag) + const +{ + return g_sProxy->GetDevices(deviceFlag); +} + +/** + * @brief The AudioDeviceDescriptor provides + * different sets of audio devices and their roles + */ +AudioDeviceDescriptor::AudioDeviceDescriptor() +{ + MEDIA_DEBUG_LOG("AudioDeviceDescriptor constructor"); + deviceType_ = DEVICE_TYPE_NONE; + deviceRole_ = DEVICE_ROLE_NONE; +} + +AudioDeviceDescriptor::~AudioDeviceDescriptor() +{ + MEDIA_DEBUG_LOG("AudioDeviceDescriptor::~AudioDeviceDescriptor"); +} + +bool AudioDeviceDescriptor::Marshalling(Parcel &parcel) const +{ + MEDIA_DEBUG_LOG("AudioDeviceDescriptor::Marshalling called"); + return parcel.WriteInt32(deviceType_) && parcel.WriteInt32(deviceRole_); +} + +AudioDeviceDescriptor *AudioDeviceDescriptor::Unmarshalling(Parcel &in) +{ + MEDIA_DEBUG_LOG("AudioDeviceDescriptor::Unmarshalling called"); + AudioDeviceDescriptor *audioDeviceDescriptor = new(std::nothrow) AudioDeviceDescriptor(); + if (audioDeviceDescriptor == nullptr) { + return nullptr; + } + audioDeviceDescriptor->deviceType_ = static_cast(in.ReadInt32()); + audioDeviceDescriptor->deviceRole_ = static_cast(in.ReadInt32()); + return audioDeviceDescriptor; +} +} // namespace AudioStandard +} // namespace OHOS diff --git a/services/src/server/audio_manager_stub.cpp b/services/src/server/audio_manager_stub.cpp old mode 100755 new mode 100644 index c5bf50ced26ca70f375bd4fbebfb381b5f6137f7..f8a7853f8356b49da96fd2f028a860f21a03101b --- a/services/src/server/audio_manager_stub.cpp +++ b/services/src/server/audio_manager_stub.cpp @@ -14,11 +14,11 @@ */ #include "audio_manager_base.h" -#include "audio_svc_manager.h" -#include "audio_device_descriptor.h" +#include "audio_system_manager.h" #include "media_log.h" namespace OHOS { +namespace AudioStandard { int AudioManagerStub::OnRemoteRequest( uint32_t code, MessageParcel &data, MessageParcel &reply, MessageOption &option) { @@ -27,49 +27,28 @@ int AudioManagerStub::OnRemoteRequest( MEDIA_ERR_LOG("caller app not acquired audio permission"); return MEDIA_PERMISSION_DENIED; } + switch (code) { - case SET_VOLUME: { - MEDIA_DEBUG_LOG("SET_VOLUME AudioManagerStub"); - int volumeType = data.ReadInt32(); - MEDIA_DEBUG_LOG("SET_VOLUME volumeType received from client= %{public}d", volumeType); - AudioSvcManager::AudioVolumeType volumeStreamConfig = - static_cast(volumeType); - MEDIA_DEBUG_LOG("SET_VOLUME volumeType= %{public}d", volumeStreamConfig); - int vol = data.ReadInt32(); - SetVolume(volumeStreamConfig, vol); - return MEDIA_OK; - } - case GET_VOLUME: { - MEDIA_DEBUG_LOG("GET_VOLUME AudioManagerStub"); - int volumeType = data.ReadInt32(); - MEDIA_DEBUG_LOG("GET_VOLUME volumeType received from client= %{public}d", volumeType); - AudioSvcManager::AudioVolumeType volumeStreamConfig = - static_cast(volumeType); - MEDIA_DEBUG_LOG("GET_VOLUME volumeType= %{public}d", volumeStreamConfig); - int ret = GetVolume(volumeStreamConfig); - reply.WriteInt32(ret); - return MEDIA_OK; - } case GET_MAX_VOLUME: { MEDIA_DEBUG_LOG("GET_MAX_VOLUME AudioManagerStub"); int volumeType = data.ReadInt32(); MEDIA_DEBUG_LOG("GET_MAX_VOLUME volumeType received from client= %{public}d", volumeType); - AudioSvcManager::AudioVolumeType volumeStreamConfig = - static_cast(volumeType); + AudioSystemManager::AudioVolumeType volumeStreamConfig = + static_cast(volumeType); MEDIA_DEBUG_LOG("GET_MAX_VOLUME volumeType= %{public}d", volumeStreamConfig); - int ret = GetMaxVolume(volumeStreamConfig); - reply.WriteInt32(ret); + float ret = GetMaxVolume(volumeStreamConfig); + reply.WriteFloat(ret); return MEDIA_OK; } case GET_MIN_VOLUME: { MEDIA_DEBUG_LOG("GET_MIN_VOLUME AudioManagerStub"); int volumeType = data.ReadInt32(); MEDIA_DEBUG_LOG("GET_MIN_VOLUME volumeType received from client= %{public}d", volumeType); - AudioSvcManager::AudioVolumeType volumeStreamConfig = - static_cast(volumeType); + AudioSystemManager::AudioVolumeType volumeStreamConfig = + static_cast(volumeType); MEDIA_DEBUG_LOG("GET_MIN_VOLUME volumeType= %{public}d", volumeStreamConfig); - int ret = GetMinVolume(volumeStreamConfig); - reply.WriteInt32(ret); + float ret = GetMinVolume(volumeStreamConfig); + reply.WriteFloat(ret); return MEDIA_OK; } case GET_DEVICES: { @@ -86,6 +65,37 @@ int AudioManagerStub::OnRemoteRequest( } return MEDIA_OK; } + case SET_AUDIO_PARAMETER: { + MEDIA_DEBUG_LOG("SET_AUDIO_PARAMETER AudioManagerStub"); + const std::string key = data.ReadString(); + const std::string value = data.ReadString(); + MEDIA_DEBUG_LOG("SET_AUDIO_PARAMETER key-value pair from client= %{public}s, %{public}s", + key.c_str(), value.c_str()); + SetAudioParameter(key, value); + return MEDIA_OK; + } + case GET_AUDIO_PARAMETER: { + MEDIA_DEBUG_LOG("GET_AUDIO_PARAMETER AudioManagerStub"); + const std::string key = data.ReadString(); + MEDIA_DEBUG_LOG("GET_AUDIO_PARAMETER key received from client= %{public}s", key.c_str()); + const std::string value = GetAudioParameter(key); + reply.WriteString(value); + return MEDIA_OK; + } + case SET_MICROPHONE_MUTE: { + MEDIA_DEBUG_LOG("SET_MICROPHONE_MUTE AudioManagerStub"); + bool isMute = data.ReadBool(); + MEDIA_DEBUG_LOG("SET_MICROPHONE_MUTE isMute value from client= %{public}d", isMute); + int32_t result = SetMicrophoneMute(isMute); + reply.WriteInt32(result); + return MEDIA_OK; + } + case IS_MICROPHONE_MUTE: { + MEDIA_DEBUG_LOG("IS_MICROPHONE_MUTE AudioManagerStub"); + bool isMute = IsMicrophoneMute(); + reply.WriteBool(isMute); + return MEDIA_OK; + } default: { MEDIA_ERR_LOG("default case, need check AudioManagerStub"); return IPCObjectStub::OnRemoteRequest(code, data, reply, option); @@ -97,4 +107,5 @@ bool AudioManagerStub::IsPermissionValid() { return true; } +} // namespace AudioStandard } // namespace OHOS diff --git a/services/src/server/audio_server.cpp b/services/src/server/audio_server.cpp old mode 100755 new mode 100644 index 4f47133672827e28a51e19e67ed6dd4296ce7307..7c92f2bc459797e33e81231b82b8dc0374a3af1e --- a/services/src/server/audio_server.cpp +++ b/services/src/server/audio_server.cpp @@ -13,31 +13,45 @@ * limitations under the License. */ +#include "audio_capturer_source.h" #include "audio_server.h" #include "iservice_registry.h" #include "media_log.h" #include "system_ability_definition.h" -using namespace std; +#define PA +#ifdef PA +extern "C" { + extern int ohos_pa_main(int argc, char *argv[]); +} +#endif namespace OHOS { - std::unordered_map AudioServer::AudioStreamVolumeMap = { - {AudioSvcManager::AudioVolumeType::STREAM_VOICE_CALL, MAX_VOLUME}, - {AudioSvcManager::AudioVolumeType::STREAM_SYSTEM, MAX_VOLUME}, - {AudioSvcManager::AudioVolumeType::STREAM_RING, MAX_VOLUME}, - {AudioSvcManager::AudioVolumeType::STREAM_MUSIC, MAX_VOLUME}, - {AudioSvcManager::AudioVolumeType::STREAM_ALARM, MAX_VOLUME}, - {AudioSvcManager::AudioVolumeType::STREAM_NOTIFICATION, MAX_VOLUME}, - {AudioSvcManager::AudioVolumeType::STREAM_BLUETOOTH_SCO, MAX_VOLUME}, - {AudioSvcManager::AudioVolumeType::STREAM_DTMF, MAX_VOLUME}, - {AudioSvcManager::AudioVolumeType::STREAM_TTS, MAX_VOLUME}, - {AudioSvcManager::AudioVolumeType::STREAM_ACCESSIBILITY, MAX_VOLUME} - }; +namespace AudioStandard { +std::map AudioServer::audioParameters; REGISTER_SYSTEM_ABILITY_BY_ID(AudioServer, AUDIO_DISTRIBUTED_SERVICE_ID, true) +#ifdef PA +constexpr int PA_ARG_COUNT = 1; + +void* AudioServer::paDaemonThread(void* arg) +{ + /* Load the mandatory pulseaudio modules at start */ + char *argv[] = { + (char*)"pulseaudio", + }; + + MEDIA_INFO_LOG("Calling ohos_pa_main\n"); + ohos_pa_main(PA_ARG_COUNT, argv); + MEDIA_INFO_LOG("Exiting ohos_pa_main\n"); + + return nullptr; +} +#endif + AudioServer::AudioServer(int32_t systemAbilityId, bool runOnCreate) - : SystemAbility(systemAbilityId, runOnCreate) + : SystemAbility(systemAbilityId, runOnCreate) {} void AudioServer::OnDump() @@ -50,52 +64,106 @@ void AudioServer::OnStart() if (res) { MEDIA_DEBUG_LOG("AudioService OnStart res=%{public}d", res); } + +#ifdef PA + int32_t ret = pthread_create(&m_paDaemonThread, nullptr, AudioServer::paDaemonThread, nullptr); + if (ret != 0) { + MEDIA_ERR_LOG("pthread_create failed %d", ret); + } + MEDIA_INFO_LOG("Created paDaemonThread\n"); +#endif } void AudioServer::OnStop() { MEDIA_DEBUG_LOG("AudioService OnStop"); -} +} -void AudioServer::SetVolume(AudioSvcManager::AudioVolumeType volumeType, int32_t volume) +void AudioServer::SetAudioParameter(const std::string key, const std::string value) { - MEDIA_DEBUG_LOG("set volume server"); - AudioServer::AudioStreamVolumeMap[volumeType] = volume; + MEDIA_DEBUG_LOG("server: set audio parameter"); + AudioServer::audioParameters[key] = value; } -int32_t AudioServer::GetVolume(AudioSvcManager::AudioVolumeType volumeType) +const std::string AudioServer::GetAudioParameter(const std::string key) { - MEDIA_DEBUG_LOG("GetVolume server volumeType=%{public}d", volumeType); - int volume = AudioServer::AudioStreamVolumeMap[volumeType]; - MEDIA_DEBUG_LOG("GetVolume server volume=%{public}d", volume); - return volume; + MEDIA_DEBUG_LOG("server: get audio parameter"); + + if (AudioServer::audioParameters.count(key)) { + return AudioServer::audioParameters[key]; + } else { + const std::string value = ""; + return value; + } } -int32_t AudioServer::GetMaxVolume(AudioSvcManager::AudioVolumeType volumeType) +float AudioServer::GetMaxVolume(AudioSystemManager::AudioVolumeType volumeType) { MEDIA_DEBUG_LOG("GetMaxVolume server"); return MAX_VOLUME; } -int32_t AudioServer::GetMinVolume(AudioSvcManager::AudioVolumeType volumeType) +float AudioServer::GetMinVolume(AudioSystemManager::AudioVolumeType volumeType) { MEDIA_DEBUG_LOG("GetMinVolume server"); return MIN_VOLUME; } +int32_t AudioServer::SetMicrophoneMute(bool isMute) +{ + AudioCapturerSource* audioCapturerSourceInstance = AudioCapturerSource::GetInstance(); + + if (audioCapturerSourceInstance->capturerInited_ == false) { + MEDIA_ERR_LOG("Capturer is not initialized. Start the recording first !"); + return ERR_INVALID_OPERATION; + } + + return audioCapturerSourceInstance->SetMute(isMute); +} + +bool AudioServer::IsMicrophoneMute() +{ + AudioCapturerSource* audioCapturerSourceInstance = AudioCapturerSource::GetInstance(); + bool isMute = false; + + if (audioCapturerSourceInstance->capturerInited_ == false) { + MEDIA_ERR_LOG("Capturer is not initialized. Start the recording first !"); + } else if (audioCapturerSourceInstance->GetMute(isMute)) { + MEDIA_ERR_LOG("GetMute status in capturer returned Error !"); + } + + return isMute; +} + std::vector> AudioServer::GetDevices(AudioDeviceDescriptor::DeviceFlag deviceFlag) { MEDIA_DEBUG_LOG("GetDevices server"); audioDeviceDescriptor_.clear(); - AudioDeviceDescriptor *audioDescriptor = new(std::nothrow) AudioDeviceDescriptor(); + sptr audioDescriptor = new(std::nothrow) AudioDeviceDescriptor(); if (audioDescriptor == nullptr) { MEDIA_ERR_LOG("new AudioDeviceDescriptor fail"); return audioDeviceDescriptor_; } - audioDescriptor->deviceType_ = AudioDeviceDescriptor::DeviceType::MIC; - audioDescriptor->deviceRole_ = AudioDeviceDescriptor::DeviceRole::INPUT_DEVICE; + if (AudioDeviceDescriptor::DeviceFlag::INPUT_DEVICES_FLAG == deviceFlag) { + audioDescriptor->deviceType_ = AudioDeviceDescriptor::DeviceType::MIC; + audioDescriptor->deviceRole_ = AudioDeviceDescriptor::DeviceRole::INPUT_DEVICE; + } else if (AudioDeviceDescriptor::DeviceFlag::OUTPUT_DEVICES_FLAG == deviceFlag) { + audioDescriptor->deviceType_ = AudioDeviceDescriptor::DeviceType::SPEAKER; + audioDescriptor->deviceRole_ = AudioDeviceDescriptor::DeviceRole::OUTPUT_DEVICE; + } else if (AudioDeviceDescriptor::DeviceFlag::ALL_DEVICES_FLAG == deviceFlag) { + sptr audioDescriptor_inputDevice = new(std::nothrow) AudioDeviceDescriptor(); + sptr audioDescriptor_outputDevice = new(std::nothrow) AudioDeviceDescriptor(); + audioDescriptor_inputDevice->deviceType_ = AudioDeviceDescriptor::DeviceType::MIC; + audioDescriptor_inputDevice->deviceRole_ = AudioDeviceDescriptor::DeviceRole::INPUT_DEVICE; + audioDeviceDescriptor_.push_back(audioDescriptor_inputDevice); + audioDescriptor_outputDevice->deviceType_ = AudioDeviceDescriptor::DeviceType::SPEAKER; + audioDescriptor_outputDevice->deviceRole_ = AudioDeviceDescriptor::DeviceRole::OUTPUT_DEVICE; + audioDeviceDescriptor_.push_back(audioDescriptor_outputDevice); + return audioDeviceDescriptor_; + } audioDeviceDescriptor_.push_back(audioDescriptor); return audioDeviceDescriptor_; } +} // namespace AudioStandard } // namespace OHOS diff --git a/services/test/audio_policy_test.cpp b/services/test/audio_policy_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9c0a6c0de4d93e74fe1595309e059b2253382924 --- /dev/null +++ b/services/test/audio_policy_test.cpp @@ -0,0 +1,219 @@ +/* + * Copyright (C) 2021 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 "audio_errors.h" +#include "audio_system_manager.h" +#include "media_log.h" + +using namespace std; +using namespace OHOS; +using namespace OHOS::AudioStandard; + +namespace AudioPolicyTest { + const int FIRST_ARG = 1; + const int SECOND_ARG = 2; + const int OPT_ARG_BASE = 10; + const int OPT_SHORT_LEN = 3; +} + +static void PrintUsage(void) +{ + cout << "NAME" << endl << endl; + cout << "\taudio_policy_test - Audio Policy Test " << endl << endl; + cout << "SYNOPSIS" << endl << endl; + cout << "\t#include " << endl << endl; + cout << "\t./audio_playback_test [OPTIONS]..." << endl << endl; + cout << "DESCRIPTION" << endl << endl; + cout << "\tControls audio volume, audio routing, audio mute" << endl << endl; + cout << "-V\n\tSets Volume for streams, -S to setStream" << endl << endl; + cout << "-v\n\tGets Volume for streams, -S to setStream" << endl << endl; + cout << "-S\n\tSet stream type" << endl << endl; + cout << "\tSupported Streams are" << endl << endl; + cout << "\t4\tMUSIC" << endl << endl; + cout << "\t3\tRING" << endl << endl; + cout << "-D\n\tSets Device Active" << endl << endl; + cout << "\tSupported Devices are" << endl << endl; + cout << "\t0\tSPEAKER" << endl << endl; + cout << "\t3\tBLUETOOTH_A2DP" << endl << endl; + cout << "\t4\tMIC" << endl << endl; + cout << "-d\n\tGets Device Active" << endl << endl; + cout << "-M\n\tSets Mute for streams, -S to setStream" << endl << endl; + cout << "-m\n\tGets Mute for streams, -S to setStream" << endl << endl; + cout << "-R\n\tSets RingerMode" << endl << endl; + cout << "-r\n\tGets RingerMode status" << endl << endl; + cout << "-s\n\tGet Stream Status" << endl << endl; + cout << "AUTHOR" << endl << endl; + cout << "\tWritten by Sajeesh Sidharthan and Anurup M" << endl << endl; +} + +static void SetStreamVolume(const AudioSystemManager *audioSystemMgr, int streamType) +{ + float volume = strtof(optarg, nullptr); + cout << "Set Volume : " << volume << endl; + int32_t result = audioSystemMgr->SetVolume(static_cast(streamType), volume); + cout << "Set Volume Result: " << result << endl; +} + +static void GetStreamVolume(const AudioSystemManager *audioSystemMgr, int streamType) +{ + float volume = audioSystemMgr->GetVolume(static_cast(streamType)); + cout << "Get Volume : " << volume << endl; +} + +static void SetStreamMute(const AudioSystemManager *audioSystemMgr, int streamType) +{ + int mute = strtol(optarg, nullptr, AudioPolicyTest::OPT_ARG_BASE); + cout << "Set Mute : " << mute << endl; + int32_t result = audioSystemMgr->SetMute(static_cast(streamType), + (mute) ? true : false); + cout << "Set Mute Result: " << result << endl; +} + +static void IsStreamMute(const AudioSystemManager *audioSystemMgr, int streamType) +{ + bool muteStatus = audioSystemMgr->IsStreamMute(static_cast(streamType)); + cout << "Get Mute : " << muteStatus << endl; +} + +static void SetStreamType(int &streamType) +{ + streamType = strtol(optarg, nullptr, AudioPolicyTest::OPT_ARG_BASE); + cout << "Set Stream : " << streamType << endl; +} + +static void IsStreamActive(const AudioSystemManager *audioSystemMgr) +{ + int streamType = strtol(optarg, nullptr, AudioPolicyTest::OPT_ARG_BASE); + cout << "Stream Active: " << audioSystemMgr->IsStreamActive( + static_cast(streamType)) << endl; +} + +static void SetDeviceActive(const AudioSystemManager *audioSystemMgr, int argc, char* argv[]) +{ + int active = -1; + int device = strtol(optarg, nullptr, AudioPolicyTest::OPT_ARG_BASE); + cout << "Set Device : " << device << endl; + + if (optind < argc && *argv[optind] != '-') { + active = strtol(argv[optind], nullptr, AudioPolicyTest::OPT_ARG_BASE); + optind++; + } + cout << "Active : " << active << endl << endl; + + int32_t result = audioSystemMgr->SetDeviceActive(AudioDeviceDescriptor::DeviceType(device), + (active) ? true : false); + cout << "Set DeviceActive Result: " << result << endl; +} + +static void IsDeviceActive(const AudioSystemManager *audioSystemMgr) +{ + int device = strtol(optarg, nullptr, AudioPolicyTest::OPT_ARG_BASE); + bool devActiveStatus = audioSystemMgr->IsDeviceActive(AudioDeviceDescriptor::DeviceType(device)); + cout << "GetDevice Active : " << devActiveStatus << endl; +} + +static void SetRingerMode(const AudioSystemManager *audioSystemMgr) +{ + int ringMode = strtol(optarg, nullptr, AudioPolicyTest::OPT_ARG_BASE); + cout << "Set Ringer Mode : " << ringMode << endl; + audioSystemMgr->SetRingerMode(static_cast(ringMode)); +} + +static void GetRingerMode(const AudioSystemManager *audioSystemMgr) +{ + int ringMode = static_cast(audioSystemMgr->GetRingerMode()); + cout << "Get Ringer Mode : " << ringMode << endl; +} + +static void NoValueError() +{ + char option[AudioPolicyTest::OPT_SHORT_LEN]; + cout << "option "; + snprintf_s(option, sizeof(option), sizeof(option) - 1, "-%c", optopt); + cout << option << " needs a value" << endl << endl; + PrintUsage(); +} + +static void UnknownOptionError() +{ + char option[AudioPolicyTest::OPT_SHORT_LEN]; + snprintf_s(option, sizeof(option), sizeof(option) - 1, "-%c", optopt); + cout << "unknown option: " << option << endl << endl; + PrintUsage(); +} + +int main(int argc, char* argv[]) +{ + int opt = 0; + if (((argc >= AudioPolicyTest::SECOND_ARG) && !strcmp(argv[AudioPolicyTest::FIRST_ARG], "--help")) || + (argc == AudioPolicyTest::FIRST_ARG)) { + PrintUsage(); + return ERR_INVALID_PARAM; + } + + int streamType = static_cast(AudioSystemManager::AudioVolumeType::STREAM_MUSIC); + AudioSystemManager *audioSystemMgr = AudioSystemManager::GetInstance(); + while ((opt = getopt(argc, argv, ":V:S:D:M:R:d:s:vmr")) != -1) { + switch (opt) { + case 'V': + SetStreamVolume(audioSystemMgr, streamType); + break; + case 'v': + GetStreamVolume(audioSystemMgr, streamType); + break; + case 'M': + SetStreamMute(audioSystemMgr, streamType); + break; + case 'm': + IsStreamMute(audioSystemMgr, streamType); + break; + case 'S': + SetStreamType(streamType); + break; + case 's': + IsStreamActive(audioSystemMgr); + break; + case 'D': + SetDeviceActive(audioSystemMgr, argc, argv); + break; + case 'd': + IsDeviceActive(audioSystemMgr); + break; + case 'R': + SetRingerMode(audioSystemMgr); + break; + case 'r': + GetRingerMode(audioSystemMgr); + break; + case ':': + NoValueError(); + break; + case '?': + UnknownOptionError(); + break; + default: + break; + } + } + + return 0; +} diff --git a/services/test/audio_recorder_test.cpp b/services/test/audio_recorder_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e7640fe6059ab06ba747a02ef9cac1bea48a08f6 --- /dev/null +++ b/services/test/audio_recorder_test.cpp @@ -0,0 +1,210 @@ +/* + * Copyright (C) 2021 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 "audio_recorder.h" +#include "media_log.h" + +using namespace std; +using namespace OHOS; +using namespace OHOS::AudioStandard; + +namespace AudioTestConstants { + constexpr int32_t SECOND_ARG_IDX = 2; + constexpr int32_t THIRD_ARG_IDX = 3; + constexpr int32_t PAUSE_BUFFER_POSITION = 512; + constexpr int32_t PAUSE_READ_TIME_SECONDS = 2; + constexpr int32_t SUCCESS = 0; +} + +class AudioRecorderTest { +public: + void CheckSupportedParams() const + { + vector supportedFormatList = AudioRecorder::GetSupportedFormats(); + MEDIA_INFO_LOG("Supported formats:"); + for (auto i = supportedFormatList.begin(); i != supportedFormatList.end(); ++i) { + MEDIA_INFO_LOG("Format %{public}d", *i); + } + + vector supportedChannelList = AudioRecorder::GetSupportedChannels(); + MEDIA_INFO_LOG("Supported channels:"); + for (auto i = supportedChannelList.begin(); i != supportedChannelList.end(); ++i) { + MEDIA_INFO_LOG("channel %{public}d", *i); + } + + vector supportedEncodingTypes + = AudioRecorder::GetSupportedEncodingTypes(); + MEDIA_INFO_LOG("Supported encoding types:"); + for (auto i = supportedEncodingTypes.begin(); i != supportedEncodingTypes.end(); ++i) { + MEDIA_INFO_LOG("encoding type %{public}d", *i); + } + + vector supportedSamplingRates = AudioRecorder::GetSupportedSamplingRates(); + MEDIA_INFO_LOG("Supported sampling rates:"); + for (auto i = supportedSamplingRates.begin(); i != supportedSamplingRates.end(); ++i) { + MEDIA_INFO_LOG("sampling rate %{public}d", *i); + } + } + + bool InitRecord(const unique_ptr &audioRecorder, const AudioRecorderParams &recorderParams) const + { + if (audioRecorder->SetParams(recorderParams) != AudioTestConstants::SUCCESS) { + MEDIA_ERR_LOG("Set audio stream parameters failed"); + audioRecorder->Release(); + return false; + } + MEDIA_INFO_LOG("Record stream created"); + + MEDIA_INFO_LOG("Starting Stream"); + if (!audioRecorder->Start()) { + MEDIA_ERR_LOG("Start stream failed"); + audioRecorder->Release(); + return false; + } + MEDIA_INFO_LOG("Recording started"); + + MEDIA_INFO_LOG("Get Audio parameters:"); + AudioRecorderParams getRecorderParams; + if (audioRecorder->GetParams(getRecorderParams) == AudioTestConstants::SUCCESS) { + MEDIA_INFO_LOG("Get Audio format: %{public}d", getRecorderParams.audioSampleFormat); + MEDIA_INFO_LOG("Get Audio sampling rate: %{public}d", getRecorderParams.samplingRate); + MEDIA_INFO_LOG("Get Audio channels: %{public}d", getRecorderParams.audioChannel); + } + + return true; + } + + bool StartRecord(const unique_ptr &audioRecorder, bool isBlocking, FILE *pFile) const + { + size_t bufferLen; + if (audioRecorder->GetBufferSize(bufferLen) < 0) { + MEDIA_ERR_LOG(" GetMinimumBufferSize failed"); + return false; + } + MEDIA_INFO_LOG("Buffer size: %{public}d", bufferLen); + + uint32_t frameCount; + if (audioRecorder->GetFrameCount(frameCount) < 0) { + MEDIA_ERR_LOG(" GetMinimumFrameCount failed"); + return false; + } + MEDIA_INFO_LOG("Frame count: %{public}d", frameCount); + + uint8_t* buffer = nullptr; + buffer = (uint8_t *) malloc(bufferLen); + + size_t size = 1; + size_t numBuffersToRecord = 1024; + int32_t bytesRead = 0; + while (numBuffersToRecord) { + bytesRead = audioRecorder->Read(*buffer, bufferLen, isBlocking); + MEDIA_INFO_LOG("Bytes read: %{public}d", bytesRead); + if (bytesRead < 0) { + break; + } + if (bytesRead > 0) { + fwrite(buffer, size, bytesRead, pFile); + numBuffersToRecord--; + MEDIA_INFO_LOG("Number of buffers to record: %{public}d", numBuffersToRecord); + if ((numBuffersToRecord == AudioTestConstants::PAUSE_BUFFER_POSITION) + && (audioRecorder->Stop())) { + MEDIA_INFO_LOG("Audio record stopped for 2 seconds"); + sleep(AudioTestConstants::PAUSE_READ_TIME_SECONDS); + MEDIA_INFO_LOG("Audio record resume"); + if (!audioRecorder->Start()) { + MEDIA_ERR_LOG("resume stream failed"); + audioRecorder->Release(); + return false; + } + } + } + } + free(buffer); + + return true; + } + + bool TestRecord(int argc, char *argv[]) const + { + MEDIA_INFO_LOG("TestCapture start "); + + unique_ptr audioRecorder = AudioRecorder::Create(AudioStreamType::STREAM_MUSIC); + + CheckSupportedParams(); + + AudioRecorderParams recorderParams; + recorderParams.audioSampleFormat = SAMPLE_S16LE; + recorderParams.samplingRate = static_cast(atoi(argv[AudioTestConstants::SECOND_ARG_IDX])); + recorderParams.audioChannel = AudioChannel::STEREO; + recorderParams.audioEncoding = ENCODING_PCM; + if (!InitRecord(audioRecorder, recorderParams)) { + MEDIA_ERR_LOG("Initialize record failed"); + return false; + } + + bool isBlocking = (atoi(argv[AudioTestConstants::THIRD_ARG_IDX]) == 1); + MEDIA_INFO_LOG("Is blocking read: %{public}s", isBlocking ? "true" : "false"); + FILE *pFile = fopen(argv[AudioTestConstants::SECOND_ARG_IDX - 1], "wb"); + if (!StartRecord(audioRecorder, isBlocking, pFile)) { + MEDIA_ERR_LOG("Start record failed"); + return false; + } + + Timestamp timestamp; + if (audioRecorder->GetAudioTime(timestamp, Timestamp::Timestampbase::MONOTONIC)) { + MEDIA_INFO_LOG("Timestamp seconds: %{public}ld", timestamp.time.tv_sec); + MEDIA_INFO_LOG("Timestamp nanoseconds: %{public}ld", timestamp.time.tv_nsec); + } + + fflush(pFile); + if (!audioRecorder->Flush()) { + MEDIA_ERR_LOG("AudioRecorderTest: flush failed"); + } + + if (!audioRecorder->Stop()) { + MEDIA_ERR_LOG("AudioRecorderTest: Stop failed"); + } + + if (!audioRecorder->Release()) { + MEDIA_ERR_LOG("AudioRecorderTest: Release failed"); + } + fclose(pFile); + MEDIA_INFO_LOG("TestCapture end"); + + return true; + } +}; + +int main(int argc, char *argv[]) +{ + MEDIA_INFO_LOG("capture test in"); + + if ((argv == nullptr) || (argc <= AudioTestConstants::THIRD_ARG_IDX)) { + MEDIA_ERR_LOG("argv is null"); + return 0; + } + + MEDIA_INFO_LOG("argc=%d", argc); + MEDIA_INFO_LOG("argv[1]=%{public}s", argv[1]); + MEDIA_INFO_LOG("argv[2]=%{public}s", argv[AudioTestConstants::SECOND_ARG_IDX]); + MEDIA_INFO_LOG("argv[3]=%{public}s", argv[AudioTestConstants::THIRD_ARG_IDX]); + + AudioRecorderTest testObj; + bool ret = testObj.TestRecord(argc, argv); + + return ret; +} diff --git a/services/test/audio_renderer_test.cpp b/services/test/audio_renderer_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9deecb7c9cb7cd36371bcfe92902b0ec073e23d4 --- /dev/null +++ b/services/test/audio_renderer_test.cpp @@ -0,0 +1,255 @@ +/* + * Copyright (C) 2021 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 "audio_renderer.h" +#include "media_log.h" +#include "pcm2wav.h" + +using namespace std; +using namespace OHOS; +using namespace OHOS::AudioStandard; + +namespace AudioTestConstants { + constexpr int32_t ARGS_INDEX_TWO = 2; + constexpr int32_t ARGS_COUNT_TWO = 2; + constexpr int32_t SUCCESS = 0; + constexpr int32_t STOP_BUFFER_POSITION = 700000; + constexpr int32_t PAUSE_BUFFER_POSITION = 1400000; + constexpr int32_t PAUSE_RENDER_TIME_SECONDS = 1; + constexpr int32_t STOP_RENDER_TIME_SECONDS = 1; +} + +class AudioRendererTest { +public: + void CheckSupportedParams() const + { + vector supportedFormatList = AudioRenderer::GetSupportedFormats(); + MEDIA_INFO_LOG("AudioRendererTest: Supported formats:"); + for (auto i = supportedFormatList.begin(); i != supportedFormatList.end(); ++i) { + MEDIA_INFO_LOG("AudioRendererTest: Format %{public}d", *i); + } + + vector supportedChannelList = AudioRenderer::GetSupportedChannels(); + MEDIA_INFO_LOG("AudioRendererTest: Supported channels:"); + for (auto i = supportedChannelList.begin(); i != supportedChannelList.end(); ++i) { + MEDIA_INFO_LOG("AudioRendererTest: channel %{public}d", *i); + } + + vector supportedEncodingTypes + = AudioRenderer::GetSupportedEncodingTypes(); + MEDIA_INFO_LOG("AudioRendererTest: Supported encoding types:"); + for (auto i = supportedEncodingTypes.begin(); i != supportedEncodingTypes.end(); ++i) { + MEDIA_INFO_LOG("AudioRendererTest: encoding type %{public}d", *i); + } + + vector supportedSamplingRates = AudioRenderer::GetSupportedSamplingRates(); + MEDIA_INFO_LOG("AudioRendererTest: Supported sampling rates:"); + for (auto i = supportedSamplingRates.begin(); i != supportedSamplingRates.end(); ++i) { + MEDIA_INFO_LOG("AudioRendererTest: sampling rate %{public}d", *i); + } + } + + bool InitRender(const unique_ptr &audioRenderer, const AudioRendererParams &rendererParams) const + { + if (audioRenderer->SetParams(rendererParams) != AudioTestConstants::SUCCESS) { + MEDIA_ERR_LOG("AudioRendererTest: Set audio renderer parameters failed"); + if (!audioRenderer->Release()) { + MEDIA_ERR_LOG("AudioRendererTest: Release failed"); + } + return false; + } + MEDIA_INFO_LOG("AudioRendererTest: Playback renderer created"); + + MEDIA_INFO_LOG("AudioRendererTest: Starting renderer"); + if (!audioRenderer->Start()) { + MEDIA_ERR_LOG("AudioRendererTest: Start failed"); + if (!audioRenderer->Release()) { + MEDIA_ERR_LOG("AudioRendererTest: Release failed"); + } + return false; + } + MEDIA_INFO_LOG("AudioRendererTest: Playback started"); + + MEDIA_INFO_LOG("AudioRendererTest: Get Audio parameters:"); + AudioRendererParams paRendererParams; + if (audioRenderer->GetParams(paRendererParams) == AudioTestConstants::SUCCESS) { + MEDIA_INFO_LOG("AudioRendererTest: Get Audio format: %{public}d", paRendererParams.sampleFormat); + MEDIA_INFO_LOG("AudioRendererTest: Get Audio sampling rate: %{public}d", paRendererParams.sampleRate); + MEDIA_INFO_LOG("AudioRendererTest: Get Audio channels: %{public}d", paRendererParams.channelCount); + } else { + MEDIA_ERR_LOG("AudioRendererTest: Get Audio parameters failed"); + } + + return true; + } + + bool StartRender(const unique_ptr &audioRenderer, FILE* wavFile) const + { + size_t bufferLen; + if (audioRenderer->GetBufferSize(bufferLen)) { + MEDIA_ERR_LOG("AudioRendererTest: GetMinimumBufferSize failed"); + return false; + } + MEDIA_DEBUG_LOG("minimum buffer length: %{public}zu", bufferLen); + + uint32_t frameCount; + if (audioRenderer->GetFrameCount(frameCount)) { + MEDIA_ERR_LOG("AudioRendererTest: GetMinimumFrameCount failed"); + return false; + } + MEDIA_INFO_LOG("AudioRendererTest: Frame count: %{public}d", frameCount); + + uint8_t* buffer = nullptr; + int32_t n = 2; + buffer = (uint8_t *) malloc(n * bufferLen); + size_t bytesToWrite = 0; + size_t bytesWritten = 0; + size_t minBytes = 4; + uint64_t latency; + bool stopTested = false; + bool pauseTested = false; + + while (!feof(wavFile)) { + bytesToWrite = fread(buffer, 1, bufferLen, wavFile); + bytesWritten = 0; + MEDIA_INFO_LOG("AudioRendererTest: Bytes to write: %{public}d", bytesToWrite); + + uint64_t currFilePos = ftell(wavFile); + MEDIA_INFO_LOG("AudioRendererTest: Current file position: %{public}llu", currFilePos); + if (!stopTested && (currFilePos > AudioTestConstants::STOP_BUFFER_POSITION) && audioRenderer->Stop()) { + MEDIA_INFO_LOG("Audio render stopping for 1 second"); + stopTested = true; + sleep(AudioTestConstants::STOP_RENDER_TIME_SECONDS); + MEDIA_INFO_LOG("Audio render resume"); + if (!audioRenderer->Start()) { + MEDIA_ERR_LOG("resume stream failed"); + break; + } + } else if (!pauseTested && (currFilePos > AudioTestConstants::PAUSE_BUFFER_POSITION) + && audioRenderer->Pause()) { + MEDIA_INFO_LOG("Audio render pausing for 1 second"); + pauseTested = true; + sleep(AudioTestConstants::PAUSE_RENDER_TIME_SECONDS); + MEDIA_INFO_LOG("Audio render resume"); + if (!audioRenderer->Flush()) { + MEDIA_ERR_LOG("AudioRendererTest: flush failed"); + break; + } + if (!audioRenderer->Start()) { + MEDIA_ERR_LOG("resume stream failed"); + break; + } + } + + if (audioRenderer->GetLatency(latency)) { + MEDIA_ERR_LOG("AudioRendererTest: GetLatency failed"); + break; + } + MEDIA_INFO_LOG("AudioRendererTest: Latency: %{public}llu", latency); + + while ((bytesWritten < bytesToWrite) && ((bytesToWrite - bytesWritten) > minBytes)) { + bytesWritten += audioRenderer->Write(buffer + bytesWritten, + bytesToWrite - bytesWritten); + MEDIA_INFO_LOG("AudioRendererTest: Bytes written: %{public}d", bytesWritten); + if (bytesWritten < 0) { + break; + } + } + } + + if (!audioRenderer->Drain()) { + MEDIA_ERR_LOG("AudioRendererTest: Drain failed"); + } + + Timestamp timeStamp; + if (audioRenderer->GetAudioTime(timeStamp, Timestamp::Timestampbase::MONOTONIC)) { + MEDIA_INFO_LOG("AudioRendererTest: Timestamp seconds: %{public}ld", timeStamp.time.tv_sec); + MEDIA_INFO_LOG("AudioRendererTest: Timestamp nanoseconds: %{public}ld", timeStamp.time.tv_nsec); + } + free(buffer); + + return true; + } + + bool TestPlayback(int argc, char *argv[]) const + { + MEDIA_INFO_LOG("AudioRendererTest: TestPlayback start "); + + int numBase = 10; + wav_hdr wavHeader; + size_t headerSize = sizeof(wav_hdr); + FILE* wavFile = fopen(argv[1], "rb"); + if (wavFile == nullptr) { + MEDIA_INFO_LOG("AudioRendererTest: Unable to open wave file"); + return false; + } + size_t bytesRead = fread(&wavHeader, 1, headerSize, wavFile); + MEDIA_INFO_LOG("AudioRendererTest: Header Read in bytes %{public}d", bytesRead); + + AudioStreamType streamType = AudioStreamType::STREAM_MUSIC; + if (argc > AudioTestConstants::ARGS_COUNT_TWO) + streamType = static_cast(strtol(argv[AudioTestConstants::ARGS_INDEX_TWO], NULL, numBase)); + unique_ptr audioRenderer = AudioRenderer::Create(streamType); + + CheckSupportedParams(); + + AudioRendererParams rendererParams; + rendererParams.sampleFormat = static_cast(wavHeader.bitsPerSample); + rendererParams.sampleRate = static_cast(wavHeader.SamplesPerSec); + rendererParams.channelCount = static_cast(wavHeader.NumOfChan); + rendererParams.encodingType = static_cast(ENCODING_PCM); + if (!InitRender(audioRenderer, rendererParams)) { + MEDIA_ERR_LOG("AudioRendererTest: Init render failed"); + return false; + } + + if (!StartRender(audioRenderer, wavFile)) { + MEDIA_ERR_LOG("AudioRendererTest: Start render failed"); + return false; + } + + if (!audioRenderer->Stop()) { + MEDIA_ERR_LOG("AudioRendererTest: Stop failed"); + } + + if (!audioRenderer->Release()) { + MEDIA_ERR_LOG("AudioRendererTest: Release failed"); + } + + fclose(wavFile); + MEDIA_INFO_LOG("AudioRendererTest: TestPlayback end"); + + return true; + } +}; + +int main(int argc, char *argv[]) +{ + MEDIA_INFO_LOG("AudioRendererTest: Render test in"); + + if ((argv == nullptr) || (argc < AudioTestConstants::ARGS_INDEX_TWO)) { + MEDIA_ERR_LOG("AudioRendererTest: argv is null"); + return 0; + } + + MEDIA_INFO_LOG("AudioRendererTest: argc=%d", argc); + MEDIA_INFO_LOG("AudioRendererTest: argv[1]=%{public}s", argv[1]); + + AudioRendererTest testObj; + bool ret = testObj.TestPlayback(argc, argv); + + return ret; +} diff --git a/services/test/pcm2wav.h b/services/test/pcm2wav.h new file mode 100644 index 0000000000000000000000000000000000000000..55aec245db6aa4b07bea136546d83b42772275f9 --- /dev/null +++ b/services/test/pcm2wav.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2021 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 +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=Sterio + 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 \ No newline at end of file diff --git a/services/test/playback_test.cpp b/services/test/playback_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5fcd97de557fe7b128f7dabe9c3dd2d77c185a07 --- /dev/null +++ b/services/test/playback_test.cpp @@ -0,0 +1,154 @@ +/* + * Copyright (C) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include + +#include "audio_service_client.h" +#include "audio_system_manager.h" +#include "media_log.h" +#include "pcm2wav.h" + +using namespace OHOS::AudioStandard; + +namespace { +constexpr uint8_t DEFAULT_FORMAT = SAMPLE_S16LE; +constexpr uint8_t DEFAULT_CHANNELS = 2; +constexpr int32_t ARGS_INDEX_TWO = 2; +constexpr int32_t ARGS_INDEX_THREE = 3; +} // namespace + +class PlaybackTest : public AudioRendererCallbacks { +public: + void OnSinkDeviceUpdatedCb() const + { + MEDIA_INFO_LOG("My custom callback OnSinkDeviceUpdatedCb"); + } + virtual void OnStreamStateChangeCb() const + { + MEDIA_INFO_LOG("My custom callback OnStreamStateChangeCb"); + } + + virtual void OnStreamBufferUnderFlowCb() const{} + virtual void OnStreamBufferOverFlowCb() const{} + virtual void OnErrorCb(AudioServiceErrorCodes error) const{} + virtual void OnEventCb(AudioServiceEventTypes error) const{} +}; + +static int32_t InitPlayback(std::unique_ptr &client, AudioStreamParams &audioParams) +{ + if (client == nullptr) { + MEDIA_ERR_LOG("Create AudioServiceClient instance failed"); + return -1; + } + + MEDIA_INFO_LOG("Initializing of AudioServiceClient"); + if (client->Initialize(AUDIO_SERVICE_CLIENT_PLAYBACK) < 0) + return -1; + + PlaybackTest customCb; + client->RegisterAudioRendererCallbacks(customCb); + + MEDIA_INFO_LOG("Creating Stream"); + if (client->CreateStream(audioParams, STREAM_MUSIC) < 0) + return -1; + + MEDIA_INFO_LOG("Starting Stream"); + if (client->StartStream() < 0) + return -1; + + return 0; +} + +int32_t StartPlayback(std::unique_ptr &client, FILE *wavFile) +{ + uint8_t* buffer = nullptr; + int32_t n = 2; + size_t bytesToWrite = 0; + size_t bytesWritten = 0; + size_t minBytes = 4; + int32_t writeError; + uint64_t timeStamp; + size_t bufferLen; + StreamBuffer stream; + + if (client->GetMinimumBufferSize(bufferLen) < 0) { + MEDIA_ERR_LOG(" GetMinimumBufferSize failed"); + return -1; + } + + MEDIA_DEBUG_LOG("minimum buffer length: %{public}zu", bufferLen); + + buffer = (uint8_t *) malloc(n * bufferLen); + while (!feof(wavFile)) { + bytesToWrite = fread(buffer, 1, bufferLen, wavFile); + bytesWritten = 0; + + while ((bytesWritten < bytesToWrite) && ((bytesToWrite - bytesWritten) > minBytes)) { + stream.buffer = buffer + bytesWritten; + stream.bufferLen = bytesToWrite - bytesWritten; + bytesWritten += client->WriteStream(stream, writeError); + if (client->GetCurrentTimeStamp(timeStamp) >= 0) + MEDIA_DEBUG_LOG("current timestamp: %{public}llu", timeStamp); + } + } + + free(buffer); + + return 0; +} + +int main(int argc, char* argv[]) +{ + wav_hdr wavHeader; + size_t headerSize = sizeof(wav_hdr); + FILE* wavFile = fopen(argv[1], "rb"); + if (wavFile == nullptr) { + fprintf(stderr, "Unable to open wave file"); + return -1; + } + + float volume = 0.5; + if (argc >= ARGS_INDEX_THREE) { + volume = strtof(argv[ARGS_INDEX_TWO], nullptr); + } + + size_t bytesRead = fread(&wavHeader, 1, headerSize, wavFile); + MEDIA_INFO_LOG("Header Read in bytes %{public}d", bytesRead); + AudioStreamParams audioParams; + audioParams.format = DEFAULT_FORMAT; + audioParams.samplingRate = wavHeader.SamplesPerSec; + audioParams.channels = DEFAULT_CHANNELS; + + std::unique_ptr client = std::make_unique(); + if (InitPlayback(client, audioParams) < 0) { + MEDIA_INFO_LOG("Initialize playback failed"); + return -1; + } + + AudioSystemManager *audioSystemMgr = AudioSystemManager::GetInstance(); + audioSystemMgr->SetVolume(AudioSystemManager::AudioVolumeType::STREAM_MUSIC, volume); + + if (StartPlayback(client, wavFile) < 0) { + MEDIA_INFO_LOG("Start playback failed"); + return -1; + } + + client->FlushStream(); + client->StopStream(); + client->ReleaseStream(); + fclose(wavFile); + MEDIA_INFO_LOG("Exit from test app"); + return 0; +} diff --git a/services/test/record_test.cpp b/services/test/record_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..506b8878a16f6602d35100e953f94f2d1f44a9ae --- /dev/null +++ b/services/test/record_test.cpp @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include + +#include "audio_service_client.h" +#include "media_log.h" + +using namespace OHOS::AudioStandard; + +namespace { +constexpr uint8_t DEFAULT_FORMAT = SAMPLE_S16LE; +constexpr uint8_t DEFAULT_CHANNELS = 2; +} // namespace + +class RecordTest : public AudioRecorderCallbacks { +public: + void OnSourceDeviceUpdatedCb() const + { + MEDIA_INFO_LOG("My custom callback OnSourceDeviceUpdatedCb"); + } + // Need to check required state changes to update applications + virtual void OnStreamStateChangeCb() const + { + MEDIA_INFO_LOG("My custom callback OnStreamStateChangeCb"); + } + + virtual void OnStreamBufferUnderFlowCb() const{} + virtual void OnStreamBufferOverFlowCb() const{} + virtual void OnErrorCb(AudioServiceErrorCodes error) const{} + virtual void OnEventCb(AudioServiceEventTypes error) const{} +}; + +int main(int argc, char* argv[]) +{ + int32_t rateArgIndex = 2; + + AudioStreamParams audioParams; + audioParams.format = DEFAULT_FORMAT; + audioParams.samplingRate = atoi(argv[rateArgIndex]); + audioParams.channels = DEFAULT_CHANNELS; + StreamBuffer stream; + + RecordTest customCb; + + std::unique_ptr client = std::make_unique(); + if (client == nullptr) { + MEDIA_ERR_LOG("Create AudioServiceClient instance failed"); + return -1; + } + + MEDIA_INFO_LOG("Initializing of AudioServiceClient"); + if (client->Initialize(AUDIO_SERVICE_CLIENT_RECORD) < 0) + return -1; + + client->RegisterAudioRecorderCallbacks(customCb); + + MEDIA_INFO_LOG("Creating Stream"); + if (client->CreateStream(audioParams, STREAM_MUSIC) < 0) { + client->ReleaseStream(); + return -1; + } + + MEDIA_INFO_LOG("Starting Stream"); + if (client->StartStream() < 0) + return -1; + + size_t bufferLen; + if (client->GetMinimumBufferSize(bufferLen) < 0) { + MEDIA_ERR_LOG(" GetMinimumBufferSize failed"); + return -1; + } + + MEDIA_DEBUG_LOG("minimum buffer length: %{public}zu", bufferLen); + + uint8_t* buffer = nullptr; + buffer = (uint8_t *) malloc(bufferLen); + FILE *pFile = fopen(argv[1], "wb"); + + size_t size = 1; + size_t numBuffersToRecord = 1024; + uint64_t timeStamp; + stream.buffer = buffer; + stream.bufferLen = bufferLen; + int32_t bytesRead = 0; + + while (numBuffersToRecord) { + bytesRead = client->ReadStream(stream, false); + if (bytesRead < 0) { + break; + } + + if (bytesRead > 0) { + fwrite(stream.buffer, size, bytesRead, pFile); + if (client->GetCurrentTimeStamp(timeStamp) >= 0) + MEDIA_DEBUG_LOG("current timestamp: %{public}llu", timeStamp); + numBuffersToRecord--; + } + } + + fflush(pFile); + client->FlushStream(); + client->StopStream(); + client->ReleaseStream(); + free(buffer); + fclose(pFile); + MEDIA_INFO_LOG("Exit from test app"); + return 0; +}