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 #ifndef LOG_TAG
16 #define LOG_TAG "AudioRenderModeCallbackTest"
17 #endif
18 
19 #include <chrono>
20 #include <cstdio>
21 #include <thread>
22 #include <climits>
23 #include <cstdlib>
24 #include "audio_renderer_log.h"
25 #include "audio_renderer.h"
26 #include "pcm2wav.h"
27 
28 using namespace std;
29 using namespace OHOS;
30 using namespace OHOS::AudioStandard;
31 
32 namespace {
33     constexpr int32_t SAMPLE_FORMAT_U8 = 8;
34     constexpr int32_t SAMPLE_FORMAT_S16LE = 16;
35     constexpr int32_t SAMPLE_FORMAT_S24LE = 24;
36     constexpr int32_t SAMPLE_FORMAT_S32LE = 32;
37     constexpr int32_t ARGS_COUNT_THREE = 3;
38     constexpr int32_t PARAM2 = 2;
39 }
40 class AudioRenderModeCallbackTest : public AudioRendererWriteCallback,
41     public enable_shared_from_this<AudioRenderModeCallbackTest> {
42 public:
OnWriteData(size_t length)43     void OnWriteData(size_t length) override
44     {
45         AUDIO_INFO_LOG("RenderCallbackTest: OnWriteData is called");
46         reqBufLen_ = length;
47         isEnqueue_ = true;
48     }
49 
GetSampleFormat(int32_t wavSampleFormat)50     AudioSampleFormat GetSampleFormat(int32_t wavSampleFormat)
51     {
52         switch (wavSampleFormat) {
53             case SAMPLE_FORMAT_U8:
54                 return AudioSampleFormat::SAMPLE_U8;
55             case SAMPLE_FORMAT_S16LE:
56                 return AudioSampleFormat::SAMPLE_S16LE;
57             case SAMPLE_FORMAT_S24LE:
58                 return AudioSampleFormat::SAMPLE_S24LE;
59             case SAMPLE_FORMAT_S32LE:
60                 return AudioSampleFormat::SAMPLE_S32LE;
61             default:
62                 return AudioSampleFormat::INVALID_WIDTH;
63         }
64     }
65 
InitRender()66     bool InitRender()
67     {
68         wav_hdr wavHeader;
69         size_t headerSize = sizeof(wav_hdr);
70         size_t bytesRead = fread(&wavHeader, 1, headerSize, wavFile_);
71         if (bytesRead != headerSize) {
72             AUDIO_ERR_LOG("RenderCallbackTest: File header reading error");
73             return false;
74         }
75 
76         AudioRendererOptions rendererOptions = {};
77         rendererOptions.streamInfo.encoding = AudioEncodingType::ENCODING_PCM;
78         rendererOptions.streamInfo.samplingRate = static_cast<AudioSamplingRate>(wavHeader.SamplesPerSec);
79         rendererOptions.streamInfo.format = GetSampleFormat(wavHeader.bitsPerSample);
80         rendererOptions.streamInfo.channels = static_cast<AudioChannel>(wavHeader.NumOfChan);
81         rendererOptions.rendererInfo.contentType = ContentType::CONTENT_TYPE_MUSIC;
82         rendererOptions.rendererInfo.streamUsage = StreamUsage::STREAM_USAGE_MEDIA;
83         rendererOptions.rendererInfo.rendererFlags = 0;
84 
85         audioRenderer_ = AudioRenderer::Create(rendererOptions);
86         if (audioRenderer_== nullptr) {
87             AUDIO_ERR_LOG("RenderCallbackTest: Renderer create failed");
88             return false;
89         }
90 
91         AUDIO_INFO_LOG("RenderCallbackTest: Playback renderer created");
92         if (audioRenderer_->SetRenderMode(RENDER_MODE_CALLBACK)) {
93             AUDIO_ERR_LOG("RenderCallbackTest: SetRenderMode failed");
94             return false;
95         }
96 
97         if (audioRenderer_->SetRendererWriteCallback(shared_from_this())) {
98             AUDIO_ERR_LOG("RenderCallbackTest: SetRendererWriteCallback failed");
99             return false;
100         }
101 
102         if (blendMode_ != 0) {
103             AUDIO_INFO_LOG("RenderCallbackTest: set blendmode to %{public}d", blendMode_);
104             audioRenderer_->SetChannelBlendMode((ChannelBlendMode)blendMode_);
105         }
106 
107         audioRenderer_->GetBufferSize(reqBufLen_);
108 
109         return true;
110     }
111 
TestPlayback(int argc,char * argv[])112     int32_t TestPlayback(int argc, char *argv[])
113     {
114         AUDIO_INFO_LOG("RenderCallbackTest: TestPlayback start");
115         if (!InitRender()) {
116             return -1;
117         }
118 
119         if (!audioRenderer_->Start()) {
120             AUDIO_ERR_LOG("RenderCallbackTest: Start failed");
121             audioRenderer_->Release();
122             return -1;
123         }
124 
125         enqueueThread_ = make_unique<thread>(&AudioRenderModeCallbackTest::EnqueueBuffer, this);
126         enqueueThread_ ->join();
127 
128         audioRenderer_->Clear();
129         audioRenderer_->Stop();
130         audioRenderer_->Release();
131         AUDIO_INFO_LOG("RenderCallbackTest: TestPlayback end");
132 
133         return 0;
134     }
135 
~AudioRenderModeCallbackTest()136     ~AudioRenderModeCallbackTest()
137     {
138         AUDIO_INFO_LOG("RenderCallbackTest: Inside ~AudioRenderModeCallbackTest");
139         if (fclose(wavFile_)) {
140             AUDIO_INFO_LOG("RenderCallbackTest: wavFile_ failed");
141         } else {
142             AUDIO_INFO_LOG("RenderCallbackTest: fclose(wavFile_) success");
143         }
144         wavFile_ = nullptr;
145 
146         if (enqueueThread_ && enqueueThread_->joinable()) {
147             enqueueThread_->join();
148             enqueueThread_ = nullptr;
149         }
150     }
151 
SetBlendMode(int32_t blendMode)152     void SetBlendMode(int32_t blendMode)
153     {
154         blendMode_ = blendMode;
155     }
156 
157     FILE *wavFile_ = nullptr;
158 private:
EnqueueBuffer()159     void EnqueueBuffer()
160     {
161         AUDIO_INFO_LOG("RenderCallbackTest: EnqueueBuffer thread");
162         while (!feof(wavFile_)) {
163             if (isEnqueue_) {
164                 // Requested length received in callback
165                 size_t reqLen = reqBufLen_;
166                 bufDesc_.buffer = nullptr;
167                 audioRenderer_->GetBufferDesc(bufDesc_);
168                 if (bufDesc_.buffer == nullptr) {
169                     continue;
170                 }
171                 // requested len in callback will never be greater than allocated buf length
172                 // This is just a fail-safe
173                 if (reqLen > bufDesc_.bufLength) {
174                     bufDesc_.dataLength = bufDesc_.bufLength;
175                 } else {
176                     bufDesc_.dataLength = reqLen;
177                 }
178 
179                 fread(bufDesc_.buffer, 1, bufDesc_.dataLength, wavFile_);
180                 audioRenderer_->Enqueue(bufDesc_);
181                 isEnqueue_ = false;
182             }
183         }
184     }
185 
186     unique_ptr<AudioRenderer> audioRenderer_ = nullptr;
187     unique_ptr<thread> enqueueThread_ = nullptr;
188     bool isEnqueue_ = true;
189     BufferDesc bufDesc_ {};
190     size_t reqBufLen_;
191     int32_t blendMode_ = 0;
192 };
193 
main(int argc,char * argv[])194 int main(int argc, char *argv[])
195 {
196     char *inputPath = argv[1];
197     char path[PATH_MAX + 1] = {0x00};
198     if ((strlen(inputPath) > PATH_MAX) || (realpath(inputPath, path) == nullptr)) {
199         AUDIO_ERR_LOG("RenderCallbackTest: Invalid input filepath");
200         return -1;
201     }
202     AUDIO_INFO_LOG("RenderCallbackTest: path = %{public}s", path);
203     auto testObj = std::make_shared<AudioRenderModeCallbackTest>();
204 
205     if (argc == ARGS_COUNT_THREE) {
206         testObj->SetBlendMode(atoi(argv[PARAM2]));
207     }
208 
209     testObj->wavFile_ = fopen(path, "rb");
210     if (testObj->wavFile_ == nullptr) {
211         AUDIO_ERR_LOG("AudioRendererTest: Unable to open wave file");
212         return -1;
213     }
214 
215     return testObj->TestPlayback(argc, argv);
216 }
217