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