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