1 /*
2 * Copyright (c) 2021-2022 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 <chrono>
17 #include <thread>
18 #include <climits>
19 #include <cstdlib>
20 #include <unistd.h>
21 #include "audio_log.h"
22 #include "interrupt_multi_renderer_test.h"
23 #include "pcm2wav.h"
24
25 using namespace std;
26
27 namespace OHOS {
28 namespace AudioStandard {
29 namespace {
30 constexpr int32_t FIVE_SEC = 5;
31 constexpr int32_t MIN_NO_OF_ARGS = 3;
32 constexpr int32_t ARG_INDEX_1 = 1;
33 constexpr int32_t ARG_INDEX_2 = 2;
34 constexpr int32_t ARG_INDEX_3 = 3;
35 constexpr int32_t ARG_INDEX_4 = 4;
36 constexpr int32_t NUM_BASE = 10;
37
38 constexpr int32_t SAMPLE_FORMAT_U8 = 8;
39 constexpr int32_t SAMPLE_FORMAT_S16LE = 16;
40 constexpr int32_t SAMPLE_FORMAT_S24LE = 24;
41 constexpr int32_t SAMPLE_FORMAT_S32LE = 32;
42 }
43
OnStateChange(const RendererState state,const StateChangeCmdType cmdType)44 void AudioRendererCallbackTestImpl::OnStateChange(const RendererState state,
45 const StateChangeCmdType __attribute__((unused)) cmdType)
46 {
47 AUDIO_DEBUG_LOG("AudioRendererCallbackTestImpl:: OnStateChange");
48
49 switch (state) {
50 case RENDERER_PREPARED:
51 AUDIO_DEBUG_LOG("AudioRendererCallbackTestImpl: OnStateChange RENDERER_PREPARED");
52 break;
53 case RENDERER_RUNNING:
54 AUDIO_DEBUG_LOG("AudioRendererCallbackTestImpl: OnStateChange RENDERER_RUNNING");
55 break;
56 case RENDERER_STOPPED:
57 AUDIO_DEBUG_LOG("AudioRendererCallbackTestImpl: OnStateChange RENDERER_STOPPED");
58 break;
59 case RENDERER_PAUSED:
60 AUDIO_DEBUG_LOG("AudioRendererCallbackTestImpl: OnStateChange RENDERER_PAUSED");
61 break;
62 case RENDERER_RELEASED:
63 AUDIO_DEBUG_LOG("AudioRendererCallbackTestImpl: OnStateChange RENDERER_RELEASED");
64 break;
65 default:
66 AUDIO_ERR_LOG("AudioRendererCallbackTestImpl: OnStateChange NOT A VALID state");
67 break;
68 }
69 }
70
OnInterrupt(const InterruptEvent & interruptEvent)71 void AudioRendererCallbackTestImpl::OnInterrupt(const InterruptEvent &interruptEvent)
72 {
73 AUDIO_DEBUG_LOG("InterruptMultiRendererTest: OnInterrupt");
74 AUDIO_DEBUG_LOG("InterruptMultiRendererTest: interrupt hintType: %{public}d", interruptEvent.hintType);
75
76 if (interruptEvent.forceType == INTERRUPT_FORCE) {
77 switch (interruptEvent.hintType) {
78 case INTERRUPT_HINT_PAUSE:
79 AUDIO_DEBUG_LOG("InterruptMultiRendererTest: ForcePaused Pause Writing");
80 isRendererPaused_ = true;
81 break;
82 case INTERRUPT_HINT_STOP:
83 AUDIO_DEBUG_LOG("InterruptMultiRendererTest: ForceStopped Stop Writing");
84 isRendererStopped_ = true;
85 break;
86 case INTERRUPT_HINT_DUCK:
87 AUDIO_INFO_LOG("InterruptMultiRendererTest: force INTERRUPT_HINT_DUCK received");
88 break;
89 case INTERRUPT_HINT_UNDUCK:
90 AUDIO_INFO_LOG("InterruptMultiRendererTest: force INTERRUPT_HINT_UNDUCK received");
91 break;
92 default:
93 AUDIO_ERR_LOG("InterruptMultiRendererTest: OnInterrupt NOT A VALID force HINT");
94 break;
95 }
96 } else if (interruptEvent.forceType == INTERRUPT_SHARE) {
97 switch (interruptEvent.hintType) {
98 case INTERRUPT_HINT_PAUSE:
99 AUDIO_DEBUG_LOG("InterruptMultiRendererTest: SharePause Hint received, Do pause if required");
100 break;
101 case INTERRUPT_HINT_RESUME:
102 AUDIO_DEBUG_LOG("InterruptMultiRendererTest: Do ShareResume");
103 isRendererResumed_ = true;
104 break;
105 default:
106 AUDIO_ERR_LOG("InterruptMultiRendererTest: OnInterrupt default share hint case");
107 break;
108 }
109 }
110 }
111
WriteBuffer(AudioRenderer * audioRenderer,FILE * wavFile,const shared_ptr<AudioRendererCallbackTestImpl> & cb) const112 void InterruptMultiRendererTest::WriteBuffer(AudioRenderer* audioRenderer, FILE* wavFile,
113 const shared_ptr<AudioRendererCallbackTestImpl> &cb) const
114 {
115 size_t bufferLen = 4096;
116 audioRenderer->GetBufferSize(bufferLen);
117
118 int32_t n = 2;
119 auto buffer = make_unique<uint8_t[]>(n * bufferLen);
120
121 size_t bytesToWrite = 0;
122 size_t bytesWritten = 0;
123 size_t minBytes = 4;
124 while (true) {
125 if (cb->isRendererResumed_) {
126 if (audioRenderer->Start()) {
127 cb->isRendererPaused_ = false;
128 }
129 }
130
131 while (!feof(wavFile) &&
132 !cb->isRendererPaused_ && !cb->isRendererStopped_ && !cb->isStopInProgress_) {
133 bytesToWrite = fread(buffer.get(), 1, bufferLen, wavFile);
134 bytesWritten = 0;
135
136 while ((bytesWritten < bytesToWrite) && ((bytesToWrite - bytesWritten) > minBytes)) {
137 int32_t retBytes = audioRenderer->Write(buffer.get() + bytesWritten,
138 bytesToWrite - bytesWritten);
139 if (retBytes < 0) {
140 AUDIO_ERR_LOG("InterruptMultiRendererTest: Error occurred in writing buffer: %{public}d", retBytes);
141 if (audioRenderer->GetStatus() == RENDERER_PAUSED) {
142 cb->isRendererPaused_ = true;
143 int32_t seekPos = bytesWritten - bytesToWrite;
144 if (fseek(wavFile, seekPos, SEEK_CUR)) {
145 AUDIO_INFO_LOG("InterruptMultiRendererTest: fseek failed");
146 }
147 AUDIO_INFO_LOG("InterruptMultiRendererTest: fseek success");
148 } else if (audioRenderer->GetStatus() == RENDERER_STOPPED) {
149 AUDIO_INFO_LOG("InterruptMultiRendererTest: Renderer Stopped");
150 cb->isRendererStopped_ = true;
151 }
152 break;
153 }
154 bytesWritten += retBytes;
155 }
156 }
157
158 if (feof(wavFile) || cb->isRendererStopped_) {
159 if (feof(wavFile)) {
160 AUDIO_INFO_LOG("InterruptMultiRendererTest: EOF reached. Complete rendering ");
161 }
162 if (audioRenderer->GetStatus() == RENDERER_RUNNING) {
163 audioRenderer->Stop();
164 }
165 audioRenderer->Release();
166 break;
167 }
168 }
169 }
170
StartRender(const unique_ptr<AudioRenderer> & audioRenderer) const171 bool InterruptMultiRendererTest::StartRender(const unique_ptr<AudioRenderer> &audioRenderer) const
172 {
173 AUDIO_INFO_LOG("InterruptMultiRendererTest: Starting renderer");
174 if (!audioRenderer->Start()) {
175 AUDIO_ERR_LOG("InterruptMultiRendererTest: Start rejected or failed");
176 if (!audioRenderer->Release()) {
177 AUDIO_ERR_LOG("InterruptMultiRendererTest: Release failed");
178 }
179 return false;
180 }
181 AUDIO_INFO_LOG("InterruptMultiRendererTest: Playback started");
182 return true;
183 }
184
GetSampleFormat(int32_t wavSampleFormat) const185 AudioSampleFormat InterruptMultiRendererTest::GetSampleFormat(int32_t wavSampleFormat) const
186 {
187 switch (wavSampleFormat) {
188 case SAMPLE_FORMAT_U8:
189 return AudioSampleFormat::SAMPLE_U8;
190 case SAMPLE_FORMAT_S16LE:
191 return AudioSampleFormat::SAMPLE_S16LE;
192 case SAMPLE_FORMAT_S24LE:
193 return AudioSampleFormat::SAMPLE_S24LE;
194 case SAMPLE_FORMAT_S32LE:
195 return AudioSampleFormat::SAMPLE_S32LE;
196 default:
197 return AudioSampleFormat::INVALID_WIDTH;
198 }
199 }
200
InitRender(const unique_ptr<AudioRenderer> & audioRenderer,FILE * & wavFile) const201 bool InterruptMultiRendererTest::InitRender(const unique_ptr<AudioRenderer> &audioRenderer, FILE* &wavFile) const
202 {
203 wav_hdr wavHeader;
204 size_t headerSize = sizeof(wav_hdr);
205 size_t bytesRead = fread(&wavHeader, 1, headerSize, wavFile);
206 if (bytesRead != headerSize) {
207 AUDIO_ERR_LOG("InterruptMultiRendererTest: File header reading error");
208 return false;
209 }
210
211 AudioRendererParams rendererParams;
212 rendererParams.sampleFormat = GetSampleFormat(wavHeader.bitsPerSample);
213 rendererParams.sampleRate = static_cast<AudioSamplingRate>(wavHeader.SamplesPerSec);
214 rendererParams.channelCount = static_cast<AudioChannel>(wavHeader.NumOfChan);
215 rendererParams.encodingType = static_cast<AudioEncodingType>(ENCODING_PCM);
216
217 if (audioRenderer->SetParams(rendererParams)) {
218 AUDIO_ERR_LOG("InterruptMultiRendererTest: Set audio renderer parameters failed");
219 if (!audioRenderer->Release()) {
220 AUDIO_ERR_LOG("InterruptMultiRendererTest: Release failed");
221 }
222 return false;
223 }
224 AUDIO_INFO_LOG("InterruptMultiRendererTest: Playback renderer created");
225
226 return true;
227 }
228
ValidateFile(char * filePath,char path[]) const229 int32_t InterruptMultiRendererTest::ValidateFile(char *filePath, char path[]) const
230 {
231 if ((strlen(filePath) > PATH_MAX) || (realpath(filePath, path) == nullptr)) {
232 AUDIO_ERR_LOG("InterruptMultiRendererTest: Invalid input filepath");
233 return -1;
234 }
235
236 return 0;
237 }
238
TestPlayback(int argc,char * argv[]) const239 int32_t InterruptMultiRendererTest::TestPlayback(int argc, char *argv[]) const
240 {
241 char *file1Path = argv[ARG_INDEX_1];
242 char path1[PATH_MAX + 1] = {0x00};
243 char *file2Path = argv[ARG_INDEX_2];
244 char path2[PATH_MAX + 1] = {0x00};
245 FILE *wavFile1 = nullptr;
246 FILE *wavFile2 = nullptr;
247
248 if (!ValidateFile(file1Path, path1)) {
249 wavFile1 = fopen(path1, "rb");
250 if (wavFile1 == nullptr) {
251 return -1;
252 }
253 }
254
255 if (!ValidateFile(file2Path, path2)) {
256 wavFile2 = fopen(path2, "rb");
257 if (wavFile2 == nullptr) {
258 fclose(wavFile1);
259 return -1;
260 }
261 }
262
263 AudioStreamType streamType1 = STREAM_MUSIC;
264 AudioStreamType streamType2 = STREAM_VOICE_CALL;
265 if (argc > MIN_NO_OF_ARGS) {
266 streamType1 = static_cast<AudioStreamType>(strtol(argv[ARG_INDEX_3], NULL, NUM_BASE));
267 }
268 if (argc > MIN_NO_OF_ARGS + 1) {
269 streamType2 = static_cast<AudioStreamType>(strtol(argv[ARG_INDEX_4], NULL, NUM_BASE));
270 }
271
272 unique_ptr<AudioRenderer> audioRenderer1 = AudioRenderer::Create(streamType1);
273 unique_ptr<AudioRenderer> audioRenderer2 = AudioRenderer::Create(streamType2);
274
275 shared_ptr<AudioRendererCallback> cb1 = make_shared<AudioRendererCallbackTestImpl>();
276 if (InitRender(audioRenderer1, wavFile1)) {
277 audioRenderer1->SetRendererCallback(cb1);
278 }
279
280 shared_ptr<AudioRendererCallback> cb2 = make_shared<AudioRendererCallbackTestImpl>();
281 if (InitRender(audioRenderer2, wavFile2)) {
282 audioRenderer2->SetRendererCallback(cb2);
283 }
284
285 std::shared_ptr<AudioRendererCallbackTestImpl> cb1Impl =
286 std::static_pointer_cast<AudioRendererCallbackTestImpl>(cb1);
287 unique_ptr<thread> writeThread1 = nullptr;
288 if (audioRenderer1->GetStatus() == RENDERER_PREPARED && StartRender(audioRenderer1)) {
289 writeThread1 = make_unique<thread>(&InterruptMultiRendererTest::WriteBuffer, this,
290 audioRenderer1.get(), wavFile1, cb1Impl);
291 this_thread::sleep_for(chrono::seconds(FIVE_SEC));
292 }
293
294 std::shared_ptr<AudioRendererCallbackTestImpl> cb2Impl =
295 std::static_pointer_cast<AudioRendererCallbackTestImpl>(cb2);
296 unique_ptr<thread> writeThread2 = nullptr;
297 if (audioRenderer2->GetStatus() == RENDERER_PREPARED && StartRender(audioRenderer2)) {
298 writeThread2 = make_unique<thread>(&InterruptMultiRendererTest::WriteBuffer, this,
299 audioRenderer2.get(), wavFile2, cb2Impl);
300 }
301
302 if (writeThread1 && writeThread1->joinable()) {
303 writeThread1->join();
304 }
305 if (writeThread2 && writeThread2->joinable()) {
306 writeThread2->join();
307 }
308 fclose(wavFile1);
309 fclose(wavFile2);
310
311 return 0;
312 }
313 } // namespace AudioStandard
314 } // namespace OHOS
315
316 using namespace OHOS;
317 using namespace OHOS::AudioStandard;
318
main(int argc,char * argv[])319 int main(int argc, char *argv[])
320 {
321 AUDIO_INFO_LOG("InterruptMultiRendererTest: Render test in");
322
323 if ((argv == nullptr) || (argc < MIN_NO_OF_ARGS)) {
324 AUDIO_ERR_LOG("InterruptMultiRendererTest: argv / argc incorrect");
325 return 0;
326 }
327
328 AUDIO_INFO_LOG("InterruptMultiRendererTest: argc=%d", argc);
329 AUDIO_INFO_LOG("InterruptMultiRendererTest: argv[1]=%{public}s", argv[ARG_INDEX_1]);
330 AUDIO_INFO_LOG("InterruptMultiRendererTest: argv[2]=%{public}s", argv[ARG_INDEX_2]);
331
332 auto interruptMultiRendererTest = make_unique<InterruptMultiRendererTest>();
333 AUDIO_INFO_LOG("InterruptMultiRendererTest: TestPlayback start ");
334 int32_t ret = interruptMultiRendererTest->TestPlayback(argc, argv);
335 AUDIO_INFO_LOG("InterruptMultiRendererTest: TestPlayback end");
336
337 return ret;
338 }
339