1 /*
2 * Copyright (c) 2022-2023 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include <cinttypes>
17 #include <ostream>
18 #include <iostream>
19 #include <thread>
20 #include <stdint.h>
21 #include <time.h>
22
23 #include "audio_service_log.h"
24 #include "audio_utils.h"
25 #include "fast/fast_audio_renderer_sink.h"
26 #include "pcm2wav.h"
27
28 using namespace std;
29 using namespace OHOS;
30 using namespace OHOS::AudioStandard;
31
32 class AudioHdiDeviceTest {
33 public:
RenderFrameFromFile()34 void RenderFrameFromFile()
35 {
36 if (hdiRenderSink_ == nullptr) {
37 AUDIO_ERR_LOG("RenderFrameFromFile hdiRenderSink_ null");
38 return;
39 }
40
41 int fd = 0;
42 uint32_t totalSizeInframe = 0;
43 uint32_t spanSizeInframe = 0;
44 uint32_t byteSizePerFrame = 0;
45
46 hdiRenderSink_->GetMmapBufferInfo(fd, totalSizeInframe, spanSizeInframe, byteSizePerFrame);
47 if (byteSizePerFrame == 0) {
48 AUDIO_ERR_LOG("RenderFrameFromFile():byteSizePerFrame is zero");
49 return;
50 }
51 if (spanSizeInframe > SIZE_MAX / byteSizePerFrame) {
52 AUDIO_ERR_LOG("RenderFrameFromFile():data overflow");
53 return;
54 }
55 size_t tempBufferSize = spanSizeInframe * byteSizePerFrame;
56 char *buffer = (char *)malloc(tempBufferSize);
57 if (buffer == nullptr) {
58 AUDIO_ERR_LOG("AudioHdiDeviceTest: failed to malloc");
59 cout << "failed to get buffer" << endl;
60 return;
61 }
62
63 uint64_t frameCount = 0;
64 int64_t timeSec = 0;
65 int64_t timeNanoSec = 0;
66
67 int64_t periodNanoSec = 5000000; // 5ms
68 int64_t fwkSyncTime = ClockTime::GetCurNano();
69
70 uint64_t written = 0;
71 int32_t ret = 0;
72 uint64_t writeCount = 0;
73 while (!stopThread && !feof(wavFile)) {
74 Trace trace1("read_write");
75 if (writeCount == 0) {
76 hdiRenderSink_->GetMmapHandlePosition(frameCount, timeSec, timeNanoSec);
77 int64_t temp = timeNanoSec + timeSec * AUDIO_NS_PER_SECOND;
78 fwkSyncTime = temp;
79 }
80 writeCount++;
81
82 fread(buffer, 1, tempBufferSize, wavFile);
83 ret = hdiRenderSink_->RenderFrame(*buffer, tempBufferSize, written);
84
85 int64_t writeTime = fwkSyncTime + writeCount * periodNanoSec + deltaTime;
86 trace1.End();
87 ClockTime::AbsoluteSleep(writeTime);
88 }
89 free(buffer);
90 }
91
InitHdiRender()92 bool InitHdiRender()
93 {
94 hdiRenderSink_ = FastAudioRendererSink::GetInstance();
95 IAudioSinkAttr attr = {};
96 attr.adapterName = "primary";
97 attr.sampleRate = 48000; // 48000hz
98 attr.channel = 2; // two channel
99 attr.format = HdiAdapterFormat::SAMPLE_S16;
100
101 hdiRenderSink_->Init(attr);
102
103 return true;
104 }
105
StartHdiRender(int32_t time)106 void StartHdiRender(int32_t time)
107 {
108 if (hdiRenderSink_ == nullptr) {
109 AUDIO_ERR_LOG("StartHdiRender hdiRenderSink_ null");
110 return;
111 }
112
113 int32_t ret = hdiRenderSink_->Start();
114 AUDIO_INFO_LOG("AudioHdiDeviceTest Start, ret %{public}d", ret);
115 float vol = 0.12; // for test
116 ret = hdiRenderSink_->SetVolume(vol, vol); // volume
117 AUDIO_INFO_LOG("AudioHdiDeviceTest set volume to 0.5, ret %{public}d", ret);
118
119 timeThread_ = make_unique<thread>(&AudioHdiDeviceTest::RenderFrameFromFile, this);
120
121 sleep(time);
122
123 cout << "stop running" << endl;
124 stopThread = true;
125 timeThread_->join();
126 hdiRenderSink_->Stop();
127 hdiRenderSink_->DeInit();
128 }
129
TestPlayback(int argc,char * argv[])130 bool TestPlayback(int argc, char *argv[])
131 {
132 AUDIO_INFO_LOG("TestPlayback in");
133
134 wav_hdr wavHeader;
135 size_t headerSize = sizeof(wav_hdr);
136 char *inputPath = argv[1];
137 char path[PATH_MAX + 1] = {0x00};
138 int32_t time = strtol(argv[2], nullptr, 10);
139 if ((strlen(inputPath) > PATH_MAX) || (realpath(inputPath, path) == nullptr)) {
140 AUDIO_ERR_LOG("Invalid path");
141 return false;
142 }
143 AUDIO_INFO_LOG("AudioHdiDeviceTest: path = %{public}s", path);
144 wavFile = fopen(path, "rb");
145 if (wavFile == nullptr) {
146 AUDIO_INFO_LOG("AudioHdiDeviceTest: Unable to open wave file");
147 return false;
148 }
149 size_t bytesRead = fread(&wavHeader, 1, headerSize, wavFile);
150 AUDIO_INFO_LOG("AudioHdiDeviceTest: Header Read in bytes %{public}zu", bytesRead);
151
152 InitHdiRender();
153 StartHdiRender(time);
154
155 return true;
156 }
157 private:
158 IMmapAudioRendererSink *hdiRenderSink_ = nullptr;
159 unique_ptr<thread> timeThread_ = nullptr;
160 int64_t deltaTime = 4000000; // 4ms
161 bool stopThread = false;
162 FILE* wavFile = nullptr;
163 };
164
165 // usage: audio_hdi_device_test /data/data/xxx.pcm 5
main(int argc,char * argv[])166 int main(int argc, char *argv[])
167 {
168 AUDIO_INFO_LOG("AudioHdiDeviceTest: Render test in");
169
170 if (argv == nullptr) {
171 AUDIO_ERR_LOG("AudioHdiDeviceTest: argv is null");
172 return 0;
173 }
174
175 int32_t argsCountThree_ = 3;
176 if (argc != argsCountThree_) {
177 AUDIO_ERR_LOG("AudioHdiDeviceTest: incorrect argc. Enter 3 args");
178 cout << "AudioHdiDeviceTest: incorrect argc" << endl;
179 return 0;
180 }
181
182 AUDIO_INFO_LOG("AudioHdiDeviceTest: argc=%{public}d", argc);
183 AUDIO_INFO_LOG("file path argv[1]=%{public}s", argv[1]);
184
185 AudioHdiDeviceTest testObj;
186 bool ret = testObj.TestPlayback(argc, argv);
187
188 return ret;
189 }
190