mirror of
https://github.com/openharmony/multimedia_audio_standard.git
synced 2026-06-30 22:17:58 -04:00
@@ -47,6 +47,7 @@
|
||||
"//foundation/multimedia/audio_framework/interfaces/inner_api/native/audiocapturer:audio_capturer_test_packages",
|
||||
"//foundation/multimedia/audio_framework/interfaces/inner_api/native/audiomanager:audio_multichannel_test",
|
||||
"//foundation/multimedia/audio_framework/interfaces/inner_api/native/audiorenderer:audio_renderer_test",
|
||||
"//foundation/multimedia/audio_framework/interfaces/inner_api/native/audiorenderer:audio_latency_accuracy_test",
|
||||
"//foundation/multimedia/audio_framework/interfaces/inner_api/native/audiorenderer:audio_render_mode_callback_test",
|
||||
"//foundation/multimedia/audio_framework/interfaces/inner_api/native/audiorenderer:audio_voip_test",
|
||||
"//foundation/multimedia/audio_framework/interfaces/inner_api/native/audiorenderer:audio_interrupt_test",
|
||||
|
||||
@@ -120,6 +120,8 @@ public:
|
||||
|
||||
int32_t GetAudioLatencyFromXml();
|
||||
|
||||
uint32_t GetSinkLatencyFromXml();
|
||||
|
||||
int32_t RegisterAudioRendererEventListener(const int32_t clientUID,
|
||||
const std::shared_ptr<AudioRendererStateChangeCallback> &callback);
|
||||
|
||||
|
||||
@@ -105,6 +105,7 @@ private:
|
||||
static const std::map<std::pair<ContentType, StreamUsage>, AudioStreamType> streamTypeMap_;
|
||||
static std::map<std::pair<ContentType, StreamUsage>, AudioStreamType> CreateStreamMap();
|
||||
bool isFirstRead_;
|
||||
bool isFirstWrite_;
|
||||
};
|
||||
} // namespace AudioStandard
|
||||
} // namespace OHOS
|
||||
|
||||
@@ -51,6 +51,7 @@ ohos_shared_library("module-hdi-sink") {
|
||||
configs = [ ":hdi_config" ]
|
||||
|
||||
cflags = [ "-DPA_MODULE_NAME=libmodule_hdi_sink_z_so" ]
|
||||
cflags += [ "-DTEST_MODE" ]
|
||||
|
||||
ldflags = [
|
||||
"-Wl",
|
||||
|
||||
@@ -59,6 +59,7 @@ struct Userdata {
|
||||
const char *adapterName;
|
||||
uint32_t buffer_size;
|
||||
uint32_t fixed_latency;
|
||||
uint32_t sink_latency;
|
||||
uint32_t render_in_idle_state;
|
||||
uint32_t open_mic_speaker;
|
||||
size_t bytes_dropped;
|
||||
@@ -78,6 +79,10 @@ struct Userdata {
|
||||
struct RendererSinkAdapter *sinkAdapter;
|
||||
pa_asyncmsgq *dq;
|
||||
pa_atomic_t dflag;
|
||||
#ifdef TEST_MODE
|
||||
uint32_t writeCount;
|
||||
uint32_t renderCount;
|
||||
#endif // TEST_MODE
|
||||
};
|
||||
|
||||
static void UserdataFree(struct Userdata *u);
|
||||
@@ -96,6 +101,13 @@ static ssize_t RenderWrite(struct Userdata *u, pa_memchunk *pchunk)
|
||||
p = pa_memblock_acquire(pchunk->memblock);
|
||||
pa_assert(p);
|
||||
|
||||
#ifdef TEST_MODE
|
||||
if (*((int*)p) > 0) {
|
||||
AUDIO_DEBUG_LOG("RenderWrite Write: %{public}d", ++u->writeCount);
|
||||
}
|
||||
AUDIO_DEBUG_LOG("RenderWrite Write renderCount: %{public}d", ++u->renderCount);
|
||||
#endif // TEST_MODE
|
||||
|
||||
while (true) {
|
||||
uint64_t writeLen = 0;
|
||||
|
||||
@@ -263,25 +275,29 @@ static void SinkUpdateRequestedLatencyCb(pa_sink *s)
|
||||
static int SinkProcessMsg(pa_msgobject *o, int code, void *data, int64_t offset,
|
||||
pa_memchunk *chunk)
|
||||
{
|
||||
AUDIO_INFO_LOG("SinkProcessMsg: code: %{public}d", code);
|
||||
struct Userdata *u = PA_SINK(o)->userdata;
|
||||
pa_assert(u);
|
||||
|
||||
AUDIO_INFO_LOG("SinkProcessMsg: code: %{public}d", code);
|
||||
switch (code) {
|
||||
case PA_SINK_MESSAGE_GET_LATENCY: {
|
||||
uint64_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 (u->sinkAdapter->RendererSinkGetLatency(&hdiLatency) == 0) {
|
||||
latency = (PA_USEC_PER_MSEC * hdiLatency);
|
||||
if (u->sink_latency) {
|
||||
*((uint64_t *)data) = u->sink_latency * PA_USEC_PER_MSEC;
|
||||
} else {
|
||||
pa_usec_t now = pa_rtclock_now();
|
||||
latency = (now - u->timestamp);
|
||||
}
|
||||
uint64_t latency;
|
||||
uint32_t hdiLatency;
|
||||
|
||||
*((uint64_t *)data) = latency;
|
||||
// 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 (u->sinkAdapter->RendererSinkGetLatency(&hdiLatency) == 0) {
|
||||
latency = (PA_USEC_PER_MSEC * hdiLatency);
|
||||
} else {
|
||||
pa_usec_t now = pa_rtclock_now();
|
||||
latency = (now - u->timestamp);
|
||||
}
|
||||
|
||||
*((uint64_t *)data) = latency;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
default:
|
||||
@@ -325,6 +341,10 @@ static int SinkSetStateInIoThreadCb(pa_sink *s, pa_sink_state_t newState,
|
||||
pa_core_exit(u->core, true, 0);
|
||||
} else {
|
||||
u->isHDISinkStarted = true;
|
||||
#ifdef TEST_MODE
|
||||
u->writeCount = 0;
|
||||
u->renderCount = 0;
|
||||
#endif // TEST_MODE
|
||||
AUDIO_INFO_LOG("Successfully restarted HDI renderer");
|
||||
}
|
||||
} else if (PA_SINK_IS_OPENED(s->thread_info.state)) {
|
||||
@@ -485,6 +505,10 @@ pa_sink *PaHdiSinkNew(pa_module *m, pa_modargs *ma, const char *driver)
|
||||
}
|
||||
|
||||
u->adapterName = pa_modargs_get_value(ma, "adapter_name", DEFAULT_DEVICE_CLASS);
|
||||
u->sink_latency = 0;
|
||||
if (pa_modargs_get_value_u32(ma, "sink_latency", &u->sink_latency) < 0) {
|
||||
AUDIO_ERR_LOG("No sink_latency argument.");
|
||||
}
|
||||
|
||||
if (pa_modargs_get_value_u32(ma, "render_in_idle_state", &u->render_in_idle_state) < 0) {
|
||||
AUDIO_ERR_LOG("Failed to parse render_in_idle_state argument.");
|
||||
@@ -498,6 +522,10 @@ pa_sink *PaHdiSinkNew(pa_module *m, pa_modargs *ma, const char *driver)
|
||||
|
||||
pa_atomic_store(&u->dflag, 0);
|
||||
u->dq = pa_asyncmsgq_new(0);
|
||||
#ifdef TEST_MODE
|
||||
u->writeCount = 0;
|
||||
u->renderCount = 0;
|
||||
#endif // TEST_MODE
|
||||
|
||||
u->sink = PaHdiSinkInit(u, ma, driver);
|
||||
if (!u->sink) {
|
||||
|
||||
@@ -40,6 +40,7 @@ PA_MODULE_USAGE(
|
||||
"file_path=<file path for data writing>"
|
||||
"adapter_name=<primary>"
|
||||
"fixed_latency=<latency measure>"
|
||||
"sink_latency=<hdi latency>"
|
||||
"render_in_idle_state<renderer state>"
|
||||
"open_mic_speaker<open mic and speaker>"
|
||||
);
|
||||
@@ -56,6 +57,7 @@ static const char * const VALID_MODARGS[] = {
|
||||
"file_path",
|
||||
"adapter_name",
|
||||
"fixed_latency",
|
||||
"sink_latency",
|
||||
"render_in_idle_state",
|
||||
"open_mic_speaker",
|
||||
NULL
|
||||
|
||||
@@ -239,6 +239,7 @@ public:
|
||||
bool RequestIndependentInterrupt(FocusType focusType);
|
||||
bool AbandonIndependentInterrupt(FocusType focusType);
|
||||
int32_t GetAudioLatencyFromXml() const;
|
||||
uint32_t GetSinkLatencyFromXml() const;
|
||||
|
||||
private:
|
||||
AudioSystemManager();
|
||||
|
||||
@@ -76,6 +76,22 @@ ohos_executable("audio_renderer_test") {
|
||||
subsystem_name = "multimedia"
|
||||
}
|
||||
|
||||
ohos_executable("audio_latency_accuracy_test") {
|
||||
install_enable = false
|
||||
|
||||
sources = [ "//foundation/multimedia/audio_framework/services/test/audio_renderer_test.cpp" ]
|
||||
|
||||
configs = [ ":audio_renderer_config" ]
|
||||
cflags = [ "-DLATENCY_ACCURACY_TEST" ]
|
||||
|
||||
deps = [ ":audio_renderer" ]
|
||||
|
||||
external_deps = [ "hiviewdfx_hilog_native:libhilog" ]
|
||||
|
||||
part_name = "multimedia_audio_framework"
|
||||
subsystem_name = "multimedia"
|
||||
}
|
||||
|
||||
ohos_executable("audio_render_mode_callback_test") {
|
||||
install_enable = false
|
||||
|
||||
|
||||
@@ -96,6 +96,8 @@ public:
|
||||
|
||||
virtual int32_t GetAudioLatencyFromXml() = 0;
|
||||
|
||||
virtual uint32_t GetSinkLatencyFromXml() = 0;
|
||||
|
||||
virtual int32_t RegisterAudioRendererEventListener(int32_t clientUID, const sptr<IRemoteObject> &object) = 0;
|
||||
|
||||
virtual int32_t UnregisterAudioRendererEventListener(int32_t clientUID) = 0;
|
||||
|
||||
@@ -62,6 +62,7 @@ private:
|
||||
void VerifyClientPermissionInternal(MessageParcel &data, MessageParcel &reply);
|
||||
void ReconfigureAudioChannelInternal(MessageParcel &data, MessageParcel &reply);
|
||||
void GetAudioLatencyFromXmlInternal(MessageParcel &data, MessageParcel &reply);
|
||||
void GetSinkLatencyFromXmlInternal(MessageParcel &data, MessageParcel &reply);
|
||||
void ReadStreamChangeInfo(MessageParcel &data, const AudioMode &mode, AudioStreamChangeInfo &streamChangeInfo);
|
||||
void RegisterAudioRendererEventListenerInternal(MessageParcel &data, MessageParcel &reply);
|
||||
void UnregisterAudioRendererEventListenerInternal(MessageParcel &data, MessageParcel &reply);
|
||||
|
||||
@@ -94,6 +94,8 @@ public:
|
||||
|
||||
int32_t GetAudioLatencyFromXml() override;
|
||||
|
||||
uint32_t GetSinkLatencyFromXml() override;
|
||||
|
||||
int32_t RegisterAudioRendererEventListener(int32_t clientUID, const sptr<IRemoteObject> &object) override;
|
||||
|
||||
int32_t UnregisterAudioRendererEventListener(int32_t clientUID) override;
|
||||
|
||||
@@ -54,6 +54,7 @@ enum AudioPolicyCommand {
|
||||
QUERY_PERMISSION,
|
||||
RECONFIGURE_CHANNEL,
|
||||
GET_AUDIO_LATENCY,
|
||||
GET_SINK_LATENCY,
|
||||
REGISTER_PLAYBACK_EVENT,
|
||||
UNREGISTER_PLAYBACK_EVENT,
|
||||
REGISTER_RECORDING_EVENT,
|
||||
|
||||
@@ -121,6 +121,8 @@ public:
|
||||
|
||||
int32_t GetAudioLatencyFromXml() override;
|
||||
|
||||
uint32_t GetSinkLatencyFromXml() override;
|
||||
|
||||
int32_t RegisterAudioRendererEventListener(int32_t clientUID, const sptr<IRemoteObject> &object) override;
|
||||
|
||||
int32_t UnregisterAudioRendererEventListener(int32_t clientUID) override;
|
||||
|
||||
@@ -80,6 +80,8 @@ public:
|
||||
|
||||
int32_t GetAudioLatencyFromXml() const;
|
||||
|
||||
uint32_t GetSinkLatencyFromXml() const;
|
||||
|
||||
// Parser callbacks
|
||||
void OnXmlParsingCompleted(const std::unordered_map<ClassType, std::list<AudioModuleInfo>> &xmldata);
|
||||
|
||||
@@ -104,6 +106,8 @@ public:
|
||||
int32_t ReconfigureAudioChannel(const uint32_t &count, DeviceType deviceType);
|
||||
|
||||
void OnAudioLatencyParsed(uint64_t latency);
|
||||
|
||||
void OnSinkLatencyParsed(uint32_t latency);
|
||||
private:
|
||||
AudioPolicyService()
|
||||
: mAudioPolicyManager(AudioPolicyManagerFactory::GetAudioPolicyManager()),
|
||||
@@ -145,6 +149,7 @@ private:
|
||||
bool interruptEnabled_ = true;
|
||||
bool isUpdateRouteSupported_ = true;
|
||||
uint64_t audioLatencyInMsec_ = 50;
|
||||
uint32_t sinkLatencyInMsec_ {0};
|
||||
std::bitset<MIN_SERVICE_COUNT> serviceFlag_;
|
||||
DeviceType mCurrentActiveDevice_ = DEVICE_TYPE_NONE;
|
||||
DeviceType mActiveInputDevice_ = DEVICE_TYPE_NONE;
|
||||
|
||||
@@ -52,6 +52,7 @@ enum NodeName {
|
||||
AUDIO_INTERRUPT_ENABLE,
|
||||
UPDATE_ROUTE_SUPPORT,
|
||||
AUDIO_LATENCY,
|
||||
SINK_LATENCY,
|
||||
UNKNOWN
|
||||
};
|
||||
|
||||
@@ -79,6 +80,7 @@ public:
|
||||
std::string channels;
|
||||
std::string bufferSize;
|
||||
std::string fixedLatency;
|
||||
std::string sinkLatency;
|
||||
std::string renderInIdleState;
|
||||
std::string OpenMicSpeaker;
|
||||
std::string fileName;
|
||||
|
||||
@@ -56,6 +56,7 @@ private:
|
||||
void ParseAudioInterrupt(xmlNode &node);
|
||||
void ParseUpdateRouteSupport(xmlNode &node);
|
||||
void ParseAudioLatency(xmlNode &node);
|
||||
void ParseSinkLatency(xmlNode &node);
|
||||
std::string ExtractPropertyValue(const std::string &propName, xmlNode &node);
|
||||
ClassType GetDeviceClassType(const std::string &deviceClass);
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ public:
|
||||
virtual void OnAudioInterruptEnable(bool enable) = 0;
|
||||
virtual void OnUpdateRouteSupport(bool isSupported) = 0;
|
||||
virtual void OnAudioLatencyParsed(uint64_t latency) = 0;
|
||||
virtual void OnSinkLatencyParsed(uint32_t latency) = 0;
|
||||
};
|
||||
} // namespace AudioStandard
|
||||
} // namespace OHOS
|
||||
|
||||
@@ -230,6 +230,7 @@ public:
|
||||
* @return returns size of audio data written in bytes.
|
||||
*/
|
||||
size_t WriteStream(const StreamBuffer &stream, int32_t &pError);
|
||||
int32_t RenderPrebuf(uint32_t writeLen);
|
||||
|
||||
/**
|
||||
* Writes audio data of the stream created using CreateStream to active sink device
|
||||
@@ -527,6 +528,9 @@ private:
|
||||
bool isContextConnected;
|
||||
bool isStreamConnected;
|
||||
|
||||
std::unique_ptr<uint8_t[]> preBuf_ {nullptr};
|
||||
uint32_t sinkLatencyInMsec_ {0};
|
||||
|
||||
int32_t clientPid_ = 0;
|
||||
int32_t clientUid_ = 0;
|
||||
|
||||
|
||||
@@ -446,6 +446,11 @@ int32_t AudioPolicyManager::GetAudioLatencyFromXml()
|
||||
return g_sProxy->GetAudioLatencyFromXml();
|
||||
}
|
||||
|
||||
uint32_t AudioPolicyManager::GetSinkLatencyFromXml()
|
||||
{
|
||||
return g_sProxy->GetSinkLatencyFromXml();
|
||||
}
|
||||
|
||||
int32_t AudioPolicyManager::GetCurrentRendererChangeInfos(
|
||||
vector<unique_ptr<AudioRendererChangeInfo>> &audioRendererChangeInfos)
|
||||
{
|
||||
|
||||
@@ -814,6 +814,26 @@ int32_t AudioPolicyProxy::GetAudioLatencyFromXml()
|
||||
return reply.ReadInt32();
|
||||
}
|
||||
|
||||
uint32_t AudioPolicyProxy::GetSinkLatencyFromXml()
|
||||
{
|
||||
MessageParcel data;
|
||||
MessageParcel reply;
|
||||
MessageOption option;
|
||||
|
||||
if (!data.WriteInterfaceToken(GetDescriptor())) {
|
||||
AUDIO_ERR_LOG("AudioPolicyProxy: GetSinkLatencyFromXml WriteInterfaceToken failed");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t error = Remote()->SendRequest(GET_SINK_LATENCY, data, reply, option);
|
||||
if (error != ERR_NONE) {
|
||||
AUDIO_ERR_LOG("GetSinkLatencyFromXml, error: %d", error);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return reply.ReadUint32();
|
||||
}
|
||||
|
||||
int32_t AudioPolicyProxy::RegisterAudioCapturerEventListener(const int32_t clientUID, const sptr<IRemoteObject> &object)
|
||||
{
|
||||
MessageParcel data;
|
||||
|
||||
@@ -339,6 +339,12 @@ void AudioPolicyManagerStub::GetAudioLatencyFromXmlInternal(MessageParcel &data,
|
||||
reply.WriteInt32(ret);
|
||||
}
|
||||
|
||||
void AudioPolicyManagerStub::GetSinkLatencyFromXmlInternal(MessageParcel &data, MessageParcel &reply)
|
||||
{
|
||||
uint32_t ret = GetSinkLatencyFromXml();
|
||||
reply.WriteUint32(ret);
|
||||
}
|
||||
|
||||
void AudioPolicyManagerStub::ReconfigureAudioChannelInternal(MessageParcel &data, MessageParcel &reply)
|
||||
{
|
||||
uint32_t count = data.ReadUint32();
|
||||
@@ -622,6 +628,10 @@ int AudioPolicyManagerStub::OnRemoteRequest(
|
||||
GetAudioLatencyFromXmlInternal(data, reply);
|
||||
break;
|
||||
|
||||
case GET_SINK_LATENCY:
|
||||
GetSinkLatencyFromXmlInternal(data, reply);
|
||||
break;
|
||||
|
||||
case REGISTER_PLAYBACK_EVENT:
|
||||
RegisterAudioRendererEventListenerInternal(data, reply);
|
||||
break;
|
||||
|
||||
@@ -1118,6 +1118,11 @@ int32_t AudioPolicyServer::GetAudioLatencyFromXml()
|
||||
return mPolicyService.GetAudioLatencyFromXml();
|
||||
}
|
||||
|
||||
uint32_t AudioPolicyServer::GetSinkLatencyFromXml()
|
||||
{
|
||||
return mPolicyService.GetSinkLatencyFromXml();
|
||||
}
|
||||
|
||||
int32_t AudioPolicyServer::RegisterAudioRendererEventListener(int32_t clientUID, const sptr<IRemoteObject> &object)
|
||||
{
|
||||
RegisterClientDeathRecipient(object, LISTENER_CLIENT);
|
||||
|
||||
@@ -677,6 +677,7 @@ void AudioPolicyService::OnServiceConnected(AudioServiceIndex serviceIndex)
|
||||
auto moduleInfoList = device.second;
|
||||
for (auto &moduleInfo : moduleInfoList) {
|
||||
AUDIO_INFO_LOG("[module_load]::Load module[%{public}s]", moduleInfo.name.c_str());
|
||||
moduleInfo.sinkLatency = sinkLatencyInMsec_ != 0 ? to_string(sinkLatencyInMsec_) : "";
|
||||
AudioIOHandle ioHandle = mAudioPolicyManager.OpenAudioPort(moduleInfo);
|
||||
if (ioHandle == OPEN_PORT_FAILURE) {
|
||||
AUDIO_INFO_LOG("[module_load]::Open port failed");
|
||||
@@ -928,6 +929,16 @@ int32_t AudioPolicyService::GetAudioLatencyFromXml() const
|
||||
return audioLatencyInMsec_;
|
||||
}
|
||||
|
||||
void AudioPolicyService::OnSinkLatencyParsed(uint32_t latency)
|
||||
{
|
||||
sinkLatencyInMsec_ = latency;
|
||||
}
|
||||
|
||||
uint32_t AudioPolicyService::GetSinkLatencyFromXml() const
|
||||
{
|
||||
return sinkLatencyInMsec_;
|
||||
}
|
||||
|
||||
void AudioPolicyService::UpdateInputDeviceInfo(DeviceType deviceType)
|
||||
{
|
||||
AUDIO_DEBUG_LOG("Current input device is %{public}d", mActiveInputDevice_);
|
||||
|
||||
@@ -70,6 +70,9 @@ bool XMLParser::ParseInternal(xmlNode &node)
|
||||
case AUDIO_LATENCY:
|
||||
ParseAudioLatency(*currNode);
|
||||
break;
|
||||
case SINK_LATENCY:
|
||||
ParseSinkLatency(*currNode);
|
||||
break;
|
||||
default:
|
||||
ParseInternal(*(currNode->children));
|
||||
break;
|
||||
@@ -215,6 +218,8 @@ NodeName XMLParser::GetNodeNameAsInt(xmlNode &node)
|
||||
return UPDATE_ROUTE_SUPPORT;
|
||||
} else if (!xmlStrcmp(node.name, reinterpret_cast<const xmlChar*>("AudioLatency"))) {
|
||||
return AUDIO_LATENCY;
|
||||
} else if (!xmlStrcmp(node.name, reinterpret_cast<const xmlChar*>("SinkLatency"))) {
|
||||
return SINK_LATENCY;
|
||||
} else {
|
||||
return UNKNOWN;
|
||||
}
|
||||
@@ -288,5 +293,15 @@ void XMLParser::ParseAudioLatency(xmlNode &node)
|
||||
|
||||
xmlFree(audioLatency);
|
||||
}
|
||||
|
||||
void XMLParser::ParseSinkLatency(xmlNode &node)
|
||||
{
|
||||
xmlNode *child = node.children;
|
||||
xmlChar *latency = xmlNodeGetContent(child);
|
||||
std::string sLatency(reinterpret_cast<char *>(latency));
|
||||
mPortObserver.OnSinkLatencyParsed((uint64_t)std::stoi(sLatency));
|
||||
|
||||
xmlFree(latency);
|
||||
}
|
||||
} // namespace AudioStandard
|
||||
} // namespace OHOS
|
||||
|
||||
@@ -302,6 +302,10 @@ std::string AudioAdapterManager::GetModuleArgs(const AudioModuleInfo &audioModul
|
||||
args.append(" file_path=");
|
||||
args.append(audioModuleInfo.fileName);
|
||||
}
|
||||
if (!audioModuleInfo.sinkLatency.empty()) {
|
||||
args.append(" sink_latency=");
|
||||
args.append(audioModuleInfo.sinkLatency);
|
||||
}
|
||||
} else if (audioModuleInfo.lib == HDI_SOURCE) {
|
||||
UpdateCommonArgs(audioModuleInfo, args);
|
||||
if (!audioModuleInfo.name.empty()) {
|
||||
|
||||
@@ -36,6 +36,9 @@ const uint32_t DOUBLE_VALUE = 2;
|
||||
const uint32_t MAX_LENGTH_FACTOR = 5;
|
||||
const uint32_t T_LENGTH_FACTOR = 4;
|
||||
const uint64_t MIN_BUF_DURATION_IN_USEC = 92880;
|
||||
const uint32_t LATENCY_THRESHOLD = 35;
|
||||
const int32_t NO_OF_PREBUF_TIMES = 6;
|
||||
|
||||
|
||||
const string PATH_SEPARATOR = "/";
|
||||
const string COOKIE_FILE_NAME = "cookie";
|
||||
@@ -780,29 +783,42 @@ int32_t AudioServiceClient::ConnectStreamToPA()
|
||||
return AUDIO_CLIENT_ERR;
|
||||
}
|
||||
uint64_t latency_in_msec = AudioSystemManager::GetInstance()->GetAudioLatencyFromXml();
|
||||
sinkLatencyInMsec_ = AudioSystemManager::GetInstance()->GetSinkLatencyFromXml();
|
||||
pa_threaded_mainloop_lock(mainLoop);
|
||||
|
||||
pa_buffer_attr bufferAttr;
|
||||
bufferAttr.fragsize = static_cast<uint32_t>(-1);
|
||||
if (latency_in_msec <= LATENCY_THRESHOLD) {
|
||||
bufferAttr.prebuf = AlignToAudioFrameSize(pa_usec_to_bytes(latency_in_msec * PA_USEC_PER_MSEC, &sampleSpec),
|
||||
sampleSpec);
|
||||
bufferAttr.maxlength = NO_OF_PREBUF_TIMES * bufferAttr.prebuf;
|
||||
bufferAttr.tlength = static_cast<uint32_t>(-1);
|
||||
} else {
|
||||
bufferAttr.prebuf = pa_usec_to_bytes(latency_in_msec * PA_USEC_PER_MSEC, &sampleSpec);
|
||||
bufferAttr.maxlength = pa_usec_to_bytes(latency_in_msec * PA_USEC_PER_MSEC * MAX_LENGTH_FACTOR, &sampleSpec);
|
||||
bufferAttr.tlength = pa_usec_to_bytes(latency_in_msec * PA_USEC_PER_MSEC * T_LENGTH_FACTOR, &sampleSpec);
|
||||
}
|
||||
bufferAttr.minreq = bufferAttr.prebuf;
|
||||
|
||||
bufferAttr.prebuf = pa_usec_to_bytes(latency_in_msec * PA_USEC_PER_MSEC, &sampleSpec);
|
||||
bufferAttr.maxlength = pa_usec_to_bytes(latency_in_msec * PA_USEC_PER_MSEC * MAX_LENGTH_FACTOR, &sampleSpec);
|
||||
bufferAttr.tlength = pa_usec_to_bytes(latency_in_msec * PA_USEC_PER_MSEC * T_LENGTH_FACTOR, &sampleSpec);
|
||||
bufferAttr.minreq = pa_usec_to_bytes(latency_in_msec * PA_USEC_PER_MSEC, &sampleSpec);
|
||||
|
||||
if (eAudioClientType == AUDIO_SERVICE_CLIENT_PLAYBACK)
|
||||
if (eAudioClientType == AUDIO_SERVICE_CLIENT_PLAYBACK) {
|
||||
result = pa_stream_connect_playback(paStream, nullptr, &bufferAttr,
|
||||
(pa_stream_flags_t)(PA_STREAM_ADJUST_LATENCY
|
||||
| PA_STREAM_INTERPOLATE_TIMING
|
||||
| PA_STREAM_START_CORKED
|
||||
| PA_STREAM_VARIABLE_RATE), nullptr, nullptr);
|
||||
else
|
||||
preBuf_ = make_unique<uint8_t[]>(bufferAttr.maxlength);
|
||||
if (preBuf_ == nullptr) {
|
||||
AUDIO_ERR_LOG("Allocate memory for buffer failed.");
|
||||
return AUDIO_CLIENT_INIT_ERR;
|
||||
}
|
||||
memset_s(preBuf_.get(), bufferAttr.maxlength, 0, bufferAttr.maxlength);
|
||||
} else {
|
||||
result = pa_stream_connect_record(paStream, nullptr, nullptr,
|
||||
(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);
|
||||
AUDIO_ERR_LOG("connection to stream error: %{public}d", error);
|
||||
@@ -1447,6 +1463,52 @@ int32_t AudioServiceClient::UpdateReadBuffer(uint8_t *buffer, size_t &length, si
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t AudioServiceClient::RenderPrebuf(uint32_t writeLen)
|
||||
{
|
||||
const pa_buffer_attr *bufferAttr = pa_stream_get_buffer_attr(paStream);
|
||||
if (bufferAttr == nullptr) {
|
||||
AUDIO_ERR_LOG("pa_stream_get_buffer_attr returned nullptr");
|
||||
return AUDIO_CLIENT_ERR;
|
||||
}
|
||||
|
||||
size_t diff = bufferAttr->maxlength - writeLen;
|
||||
if (diff <= 0) {
|
||||
return AUDIO_CLIENT_SUCCESS;
|
||||
}
|
||||
|
||||
int32_t writeError;
|
||||
StreamBuffer prebufStream;
|
||||
prebufStream.buffer = preBuf_.get();
|
||||
uint32_t extra {0};
|
||||
if (writeLen == 0) {
|
||||
return AUDIO_CLIENT_SUCCESS;
|
||||
} else if (writeLen > diff) {
|
||||
prebufStream.bufferLen = diff;
|
||||
} else {
|
||||
prebufStream.bufferLen = writeLen;
|
||||
extra = diff % writeLen;
|
||||
}
|
||||
|
||||
size_t bytesWritten {0};
|
||||
while (true) {
|
||||
bytesWritten += WriteStream(prebufStream, writeError);
|
||||
if (writeError) {
|
||||
AUDIO_ERR_LOG("RenderPrebuf failed: %{public}d", writeError);
|
||||
return AUDIO_CLIENT_ERR;
|
||||
}
|
||||
|
||||
if ((diff - bytesWritten) <= 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
if ((diff - bytesWritten) == extra) {
|
||||
prebufStream.bufferLen = extra;
|
||||
}
|
||||
}
|
||||
|
||||
return AUDIO_CLIENT_SUCCESS;
|
||||
}
|
||||
|
||||
void AudioServiceClient::OnTimeOut()
|
||||
{
|
||||
AUDIO_ERR_LOG("Inside read timeout callback");
|
||||
@@ -1742,27 +1804,24 @@ int32_t AudioServiceClient::GetAudioLatency(uint64_t &latency) const
|
||||
return AUDIO_CLIENT_PA_ERR;
|
||||
}
|
||||
|
||||
pa_usec_t paLatency;
|
||||
pa_usec_t cacheLatency;
|
||||
int32_t retVal = AUDIO_CLIENT_SUCCESS;
|
||||
int negative = 0;
|
||||
pa_usec_t paLatency {0};
|
||||
pa_usec_t cacheLatency {0};
|
||||
int negative {0};
|
||||
|
||||
// Get PA latency
|
||||
pa_threaded_mainloop_lock(mainLoop);
|
||||
|
||||
pa_operation *operation = pa_stream_update_timing_info(paStream, NULL, NULL);
|
||||
if (operation != nullptr) {
|
||||
pa_operation_unref(operation);
|
||||
} else {
|
||||
AUDIO_ERR_LOG("pa_stream_update_timing_info failed");
|
||||
}
|
||||
|
||||
while (true) {
|
||||
pa_operation *operation = pa_stream_update_timing_info(paStream, NULL, NULL);
|
||||
if (operation != nullptr) {
|
||||
pa_operation_unref(operation);
|
||||
} else {
|
||||
AUDIO_ERR_LOG("pa_stream_update_timing_info failed");
|
||||
}
|
||||
if (pa_stream_get_latency(paStream, &paLatency, &negative) >= 0) {
|
||||
if (negative) {
|
||||
latency = 0;
|
||||
retVal = AUDIO_CLIENT_ERR;
|
||||
return retVal;
|
||||
pa_threaded_mainloop_unlock(mainLoop);
|
||||
return AUDIO_CLIENT_ERR;
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -1776,7 +1835,7 @@ int32_t AudioServiceClient::GetAudioLatency(uint64_t &latency) const
|
||||
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;
|
||||
latency = paLatency + cacheLatency - (sinkLatencyInMsec_ * PA_USEC_PER_MSEC);
|
||||
AUDIO_INFO_LOG("total latency: %{public}" PRIu64 ", pa latency: %{public}"
|
||||
PRIu64 ", cache latency: %{public}" PRIu64, latency, paLatency, cacheLatency);
|
||||
} else if (eAudioClientType == AUDIO_SERVICE_CLIENT_RECORD) {
|
||||
@@ -1788,7 +1847,7 @@ int32_t AudioServiceClient::GetAudioLatency(uint64_t &latency) const
|
||||
AUDIO_INFO_LOG("total latency: %{public}" PRIu64 ", pa latency: %{public}" PRIu64, latency, paLatency);
|
||||
}
|
||||
|
||||
return retVal;
|
||||
return AUDIO_CLIENT_SUCCESS;
|
||||
}
|
||||
|
||||
void AudioServiceClient::RegisterAudioRendererCallbacks(const AudioRendererCallbacks &cb)
|
||||
|
||||
@@ -91,7 +91,8 @@ AudioStream::AudioStream(AudioStreamType eStreamType, AudioMode eMode, int32_t a
|
||||
captureMode_(CAPTURE_MODE_NORMAL),
|
||||
isReadyToWrite_(false),
|
||||
isReadyToRead_(false),
|
||||
isFirstRead_(false)
|
||||
isFirstRead_(false),
|
||||
isFirstWrite_(false)
|
||||
{
|
||||
AUDIO_DEBUG_LOG("AudioStream ctor, appUID = %{public}d", appUid);
|
||||
audioStreamTracker_ = std::make_unique<AudioStreamTracker>(eMode, appUid);
|
||||
@@ -366,6 +367,7 @@ bool AudioStream::StartAudioStream()
|
||||
}
|
||||
|
||||
isFirstRead_ = true;
|
||||
isFirstWrite_ = true;
|
||||
state_ = RUNNING;
|
||||
AUDIO_INFO_LOG("StartAudioStream SUCCESS");
|
||||
|
||||
@@ -431,6 +433,12 @@ size_t AudioStream::Write(uint8_t *buffer, size_t buffer_size)
|
||||
stream.buffer = buffer;
|
||||
stream.bufferLen = buffer_size;
|
||||
isWriteInProgress_ = true;
|
||||
if (isFirstWrite_) {
|
||||
if (RenderPrebuf(stream.bufferLen)) {
|
||||
return ERR_WRITE_FAILED;
|
||||
}
|
||||
isFirstWrite_ = false;
|
||||
}
|
||||
size_t bytesWritten = WriteStream(stream, writeError);
|
||||
isWriteInProgress_ = false;
|
||||
if (writeError != 0) {
|
||||
|
||||
@@ -695,5 +695,10 @@ int32_t AudioSystemManager::GetAudioLatencyFromXml() const
|
||||
{
|
||||
return AudioPolicyManager::GetInstance().GetAudioLatencyFromXml();
|
||||
}
|
||||
|
||||
uint32_t AudioSystemManager::GetSinkLatencyFromXml() const
|
||||
{
|
||||
return AudioPolicyManager::GetInstance().GetSinkLatencyFromXml();
|
||||
}
|
||||
} // namespace AudioStandard
|
||||
} // namespace OHOS
|
||||
|
||||
@@ -12,6 +12,9 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
#ifdef LATENCY_ACCURACY_TEST
|
||||
#include <cinttypes>
|
||||
#endif // LATENCY_ACCURACY_TEST
|
||||
#include <vector>
|
||||
|
||||
#include "audio_renderer.h"
|
||||
@@ -29,11 +32,13 @@ namespace {
|
||||
constexpr int32_t ARGS_COUNT_THREE = 3;
|
||||
constexpr int32_t ARGS_COUNT_FOUR = 4;
|
||||
constexpr int32_t SUCCESS = 0;
|
||||
#ifndef LATENCY_ACCURACY_TEST
|
||||
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;
|
||||
constexpr float TRACK_VOLUME = 0.2f;
|
||||
#endif // LATENCY_ACCURACY_TEST
|
||||
|
||||
constexpr int32_t SAMPLE_FORMAT_U8 = 8;
|
||||
constexpr int32_t SAMPLE_FORMAT_S16LE = 16;
|
||||
@@ -135,14 +140,16 @@ public:
|
||||
return false;
|
||||
}
|
||||
AUDIO_INFO_LOG("AudioRendererTest: Playback started");
|
||||
|
||||
#ifndef LATENCY_ACCURACY_TEST
|
||||
if (audioRenderer->SetVolume(TRACK_VOLUME) == SUCCESS) {
|
||||
AUDIO_INFO_LOG("AudioRendererTest: volume set to: %{public}f", audioRenderer->GetVolume());
|
||||
}
|
||||
#endif // LATENCY_ACCURACY_TEST
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifndef LATENCY_ACCURACY_TEST
|
||||
bool TestPauseStop(const unique_ptr<AudioRenderer> &audioRenderer, bool &pauseTested, bool &stopTested,
|
||||
FILE &wavFile) const
|
||||
{
|
||||
@@ -176,6 +183,7 @@ public:
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif // LATENCY_ACCURACY_TEST
|
||||
|
||||
bool GetBufferLen(const unique_ptr<AudioRenderer> &audioRenderer, size_t &bufferLen) const
|
||||
{
|
||||
@@ -210,23 +218,27 @@ public:
|
||||
size_t bytesWritten = 0;
|
||||
size_t minBytes = 4;
|
||||
uint64_t latency;
|
||||
#ifndef LATENCY_ACCURACY_TEST
|
||||
bool stopTested = false;
|
||||
bool pauseTested = false;
|
||||
#endif // LATENCY_ACCURACY_TEST
|
||||
#ifdef LATENCY_ACCURACY_TEST
|
||||
uint32_t writeCount {0};
|
||||
#endif // LATENCY_ACCURACY_TEST
|
||||
|
||||
while (!feof(wavFile)) {
|
||||
bytesToWrite = fread(buffer.get(), 1, bufferLen, wavFile);
|
||||
bytesWritten = 0;
|
||||
AUDIO_INFO_LOG("AudioRendererTest: Bytes to write: %{public}zu", bytesToWrite);
|
||||
|
||||
#ifndef LATENCY_ACCURACY_TEST
|
||||
if (!TestPauseStop(audioRenderer, pauseTested, stopTested, *wavFile)) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (audioRenderer->GetLatency(latency)) {
|
||||
AUDIO_ERR_LOG("AudioRendererTest: GetLatency failed");
|
||||
break;
|
||||
}
|
||||
|
||||
#endif // LATENCY_ACCURACY_TEST
|
||||
#ifdef LATENCY_ACCURACY_TEST
|
||||
AUDIO_DEBUG_LOG("start: %{public}d", ++writeCount);
|
||||
#endif // LATENCY_ACCURACY_TEST
|
||||
while ((bytesWritten < bytesToWrite) && ((bytesToWrite - bytesWritten) > minBytes)) {
|
||||
bytesWritten += audioRenderer->Write(buffer.get() + bytesWritten,
|
||||
bytesToWrite - bytesWritten);
|
||||
@@ -235,6 +247,18 @@ public:
|
||||
break;
|
||||
}
|
||||
}
|
||||
#ifdef LATENCY_ACCURACY_TEST
|
||||
AUDIO_DEBUG_LOG("complete: %{public}d", writeCount);
|
||||
#endif // LATENCY_ACCURACY_TEST
|
||||
|
||||
if (audioRenderer->GetLatency(latency)) {
|
||||
AUDIO_ERR_LOG("AudioRendererTest: GetLatency failed");
|
||||
break;
|
||||
#if LATENCY_ACCURACY_TEST
|
||||
} else {
|
||||
AUDIO_DEBUG_LOG("GetLatency: %{public}" PRIu64, latency);
|
||||
#endif // LATENCY_ACCURACY_TEST
|
||||
}
|
||||
}
|
||||
|
||||
if (!audioRenderer->Drain()) {
|
||||
|
||||
Reference in New Issue
Block a user