1/*
2 * Copyright (c) 2020-2021 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 <ctime>
17#include <iostream>
18#include <string>
19#include <thread>
20#include <unistd.h>
21#include "audio_capturer.h"
22#include "media_errors.h"
23#include "securec.h"
24
25using namespace OHOS;
26using namespace OHOS::Audio;
27using namespace OHOS::Media;
28using namespace std;
29
30struct AudioSourceInput {
31    size_t framesize;
32    uint8_t *buffer;
33    AudioCapturer *audioCap;
34    AudioCodecFormat audioFormat;
35    std::thread processThread;
36    bool bThreadRun;
37};
38
39enum AppState {
40    AV_ON,
41    AV_OFF
42};
43
44static AudioSourceInput g_audioSourceProcessInput;
45
46static char *GernerateFileName(AudioCodecFormat format)
47{
48    time_t stCurrent;
49    char aszDatetime[0x100];
50    (void)time(&stCurrent);
51    struct tm *pstCurrentTime = localtime(&(stCurrent));
52    if (pstCurrentTime == nullptr) {
53        return nullptr;
54    }
55    if (strftime(aszDatetime, 0x100, "%Y-%m-%d-%H-%M-%S", pstCurrentTime) > 0) {
56        std::cout << "Current Time: " << aszDatetime << std::endl;
57    }
58
59    string postfix;
60    switch (format) {
61        case PCM:
62            postfix = "pcm";
63            break;
64        case AAC_LC:
65            postfix = "aac";
66            break;
67        case G711A:
68            postfix = "g711a";
69            break;
70        case G711U:
71            postfix = "g711u";
72            break;
73        case G726:
74            postfix = "g726";
75            break;
76        default:
77            return nullptr;
78    }
79
80    const int32_t size = 0x180;
81    char *name = static_cast<char *>(malloc(size));
82    if (name == nullptr) {
83        return nullptr;
84    }
85    (void)memset_s(name, size, 0, size);
86    /* create file for save stream */
87    if (snprintf_s(name, size, size - 1, "/userdata/audio_%s.%s", aszDatetime, postfix.c_str()) < 0) {
88        std::cout << "snprintf_s failed " << std::endl;
89        free(name);
90        return nullptr;
91    }
92    return name;
93}
94
95static void AudioInputSourceProcess(AudioSourceInput *audioSourceInput)
96{
97    const int32_t waitTimeUs = 20000;
98    std::cout << "audioSourceInput: " << audioSourceInput << std::endl;
99    if (audioSourceInput == nullptr) {
100        return;
101    }
102
103    char *fileName = GernerateFileName(audioSourceInput->audioFormat);
104    if (fileName == nullptr) {
105        return;
106    }
107
108    FILE *pfd = fopen(fileName, "w+");
109    if (pfd == nullptr) {
110        std::cout << "open file  failed " << fileName << std::endl;
111        free(fileName);
112        return;
113    }
114    std::cout << "Open  SUCCESS " << fileName << std::endl;
115    int readCnt = 0;
116    while (audioSourceInput->bThreadRun) {
117        int ret = audioSourceInput->audioCap->Read(audioSourceInput->buffer,
118            audioSourceInput->framesize, false);
119        if (ret <= 0) {
120            std::cout << "audioCap Read failed ret:" << ret << std::endl;
121            usleep(waitTimeUs);
122            continue;
123        }
124        if (fwrite(audioSourceInput->buffer, 1, ret, pfd) != ret) {
125            std::cout << "fwrite failed errno:"<< errno << std::endl;
126            break;
127        }
128        readCnt++;
129        std::cout << "audioCap Read readCnt:  "<< readCnt << " size: " << ret << std::endl;
130    }
131    (void)fclose(pfd);
132    free(fileName);
133}
134
135struct CapturerInfo {
136    AudioCodecFormat audioFormat;
137    int32_t sampleRate;
138    int32_t bitRate;
139};
140
141static AudioCodecFormat GetAudioFormat(void)
142{
143    std::cout << "*******************************************" << endl;
144    std::cout << "SetCapturerInfo (PCM:1, AAC_LC:2, G711A:7, G711U:8, G726:9)" << endl;
145    std::cout << "*******************************************" << endl;
146    int32_t audioFormat;
147    std::cin >> audioFormat;
148    cout << "input  audioFormat:" << audioFormat << endl;
149    if (audioFormat != 1 && audioFormat != 0x2 && audioFormat != 0x7 &&
150        audioFormat != 0x8 && audioFormat != 0x9) {
151        std::cout << "Can't support input format:" << static_cast<int32_t> (audioFormat) << std::endl;
152        return FORMAT_BUTT;
153    }
154    return static_cast<AudioCodecFormat> (audioFormat);
155}
156
157static void GetDefaultSampleRateAndRateBaseFormat(AudioCodecFormat format, int32_t &sr, int32_t &rate)
158{
159    const CapturerInfo audioCapturerInfo[] = {
160        {PCM, 16000, 128000},
161        {AAC_LC, 48000, 128000},
162        {G711A, 8000, 64000},
163        {G711U, 8000, 64000},
164        {G726, 8000, 24000}
165    };
166
167    int validCapInfoNum = sizeof(audioCapturerInfo) / sizeof(audioCapturerInfo[0]);
168    for (int i = 0; i < validCapInfoNum; i++) {
169        if (format == audioCapturerInfo[i].audioFormat) {
170            sr = audioCapturerInfo[i].sampleRate;
171            rate = audioCapturerInfo[i].bitRate;
172        }
173    }
174}
175
176static int32_t GetChannelCount(void)
177{
178    std::cout << "*******************************************" << endl;
179    std::cout << "SetCapturerInfo (channelCount:1, channelCount:2)" << endl;
180    std::cout << "*******************************************" << endl;
181    int32_t channelCount;
182    std::cin >> channelCount;
183    if (channelCount != 1 && channelCount != 0x2) {
184        std::cout << "Can't support input channelCount:" << channelCount << std::endl;
185        return -1;
186    }
187    return channelCount;
188}
189
190static void ShowCmdInfo(void)
191{
192    cout << "*******************************************" << endl;
193    cout << "Select the behavior of audio capturer." << endl;
194    cout << "s or S-----start audio capturer" << endl;
195    cout << "p or P-----stop audio capturer" << endl;
196    cout << "q or Q-----quit audio capturer" << endl;
197    cout << "*******************************************" << endl;
198}
199
200static void TaskQuit(AudioCapturer &audioCap, AppState &state)
201{
202    if (state == AV_ON) {
203        g_audioSourceProcessInput.bThreadRun = false;
204        g_audioSourceProcessInput.processThread.join();
205        if (!audioCap.Stop()) {
206            std::cout << "Stop audioCap failed, quit record\n" << endl;
207        }
208        state = AV_OFF;
209    }
210}
211
212static int32_t TaskStop(AudioCapturer &audioCap, AppState &state)
213{
214    if (state == AV_ON) {
215        g_audioSourceProcessInput.bThreadRun = false;
216        g_audioSourceProcessInput.processThread.join();
217        if (!audioCap.Stop()) {
218            std::cout << "Stop audioCap fialed, stop record " << endl;
219            return -1;
220        }
221        state = AV_OFF;
222    } else {
223        std::cout << "Start recorder first." << endl;
224    }
225    return 0;
226}
227
228static int32_t TaskStart(AudioCapturer &audioCap, AppState &state)
229{
230    if (state == AV_ON) {
231        return 0;
232    }
233
234    if (!audioCap.Start()) {
235        std::cout << "Can't Start..." << endl;
236        delete g_audioSourceProcessInput.buffer;
237        return -1;
238    }
239    g_audioSourceProcessInput.audioCap = &audioCap;
240    g_audioSourceProcessInput.bThreadRun = true;
241    g_audioSourceProcessInput.processThread = std::thread(AudioInputSourceProcess,
242        &g_audioSourceProcessInput);
243    state = AV_ON;
244    std::cout << "Recording..." << endl;
245    return 0;
246}
247
248static void RumCmd(AudioCapturer &audioCap)
249{
250    ShowCmdInfo();
251    char input;
252    AppState state = AV_OFF;
253    while (std::cin >> input) {
254        switch (input) {
255            case 's':
256            case 'S':
257                if (TaskStart(audioCap, state) != 0) {
258                    return;
259                }
260                break;
261            case 'p':
262            case 'P':
263                if (TaskStop(audioCap, state) != 0) {
264                    return;
265                }
266                break;
267            case 'q':
268            case 'Q':
269                TaskQuit(audioCap, state);
270                return;
271            default:
272                break;
273        }
274    }
275}
276
277int main(int argc, char *argv[])
278{
279    std::cout << "audio_capture_sample " << std::endl;
280    int ret = 0;
281    size_t frameCount;
282    AudioCapturer audioCap;
283
284    AudioCapturerInfo info;
285    info.inputSource = AUDIO_MIC;
286    info.bitWidth = BIT_WIDTH_16;
287    AudioCodecFormat audioFormat = GetAudioFormat();
288    if (audioFormat == FORMAT_BUTT) {
289        return -1;
290    }
291    info.audioFormat = audioFormat;
292    g_audioSourceProcessInput.audioFormat = audioFormat;
293    GetDefaultSampleRateAndRateBaseFormat(audioFormat, info.sampleRate, info.bitRate);
294
295    info.channelCount = GetChannelCount();
296    if (info.channelCount == -1) {
297        return -1;
298    }
299
300    std::cout << " SetCapturerInfo" << std::endl;
301    if ((ret = audioCap.SetCapturerInfo(info)) != 0) {
302        std::cout << "Can't SetCapturerInfo " << std::endl;
303        delete g_audioSourceProcessInput.buffer;
304        return -1;
305    }
306    frameCount = audioCap.GetFrameCount();
307    std::cout << "GetFrameCount  " << frameCount << std::endl;
308    g_audioSourceProcessInput.framesize = frameCount * 0x400;
309    g_audioSourceProcessInput.buffer = new uint8_t[g_audioSourceProcessInput.framesize];
310
311    RumCmd(audioCap);
312
313    audioCap.Release();
314    delete g_audioSourceProcessInput.buffer;
315    g_audioSourceProcessInput.buffer = nullptr;
316    return 0;
317}
318