1 /*
2  * Copyright (C) 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 "avcodec_audio_g711mu_encoder_demo.h"
17 #include <iostream>
18 #include <unistd.h>
19 #include <chrono>
20 #include "securec.h"
21 #include "avcodec_common.h"
22 #include "avcodec_errors.h"
23 #include "media_description.h"
24 #include "native_avformat.h"
25 #include "demo_log.h"
26 #include "avcodec_codec_name.h"
27 #include "avcodec_audio_common.h"
28 
29 using namespace OHOS;
30 using namespace OHOS::MediaAVCodec;
31 using namespace OHOS::MediaAVCodec::AudioG711muDemo;
32 using namespace std;
33 namespace {
34 constexpr uint32_t CHANNEL_COUNT = 1;
35 constexpr uint32_t SAMPLE_RATE = 8000;
36 constexpr uint32_t FRAME_DURATION_US = 33000;
37 constexpr int32_t SAMPLE_FORMAT = AudioSampleFormat::SAMPLE_S16LE;
38 constexpr int32_t INPUT_FRAME_BYTES = 320; // 20ms
39 
40 constexpr string_view INPUT_FILE_PATH = "/data/test/media/g711mu_8kHz_10s.pcm";
41 constexpr string_view OUTPUT_FILE_PATH = "/data/test/media/g711mu_8kHz_10s_afterEncode.raw";
42 } // namespace
43 
OnError(OH_AVCodec * codec,int32_t errorCode,void * userData)44 static void OnError(OH_AVCodec *codec, int32_t errorCode, void *userData)
45 {
46     (void)codec;
47     (void)errorCode;
48     (void)userData;
49     cout << "Error received, errorCode:" << errorCode << endl;
50 }
51 
OnOutputFormatChanged(OH_AVCodec * codec,OH_AVFormat * format,void * userData)52 static void OnOutputFormatChanged(OH_AVCodec *codec, OH_AVFormat *format, void *userData)
53 {
54     (void)codec;
55     (void)format;
56     (void)userData;
57     cout << "OnOutputFormatChanged received" << endl;
58 }
59 
OnInputBufferAvailable(OH_AVCodec * codec,uint32_t index,OH_AVMemory * data,void * userData)60 static void OnInputBufferAvailable(OH_AVCodec *codec, uint32_t index, OH_AVMemory *data, void *userData)
61 {
62     (void)codec;
63     AEncSignal *signal = static_cast<AEncSignal *>(userData);
64     unique_lock<mutex> lock(signal->inMutex_);
65     signal->inQueue_.push(index);
66     signal->inBufferQueue_.push(data);
67     signal->inCond_.notify_all();
68 }
69 
OnOutputBufferAvailable(OH_AVCodec * codec,uint32_t index,OH_AVMemory * data,OH_AVCodecBufferAttr * attr,void * userData)70 static void OnOutputBufferAvailable(OH_AVCodec *codec, uint32_t index, OH_AVMemory *data, OH_AVCodecBufferAttr *attr,
71                                     void *userData)
72 {
73     (void)codec;
74     AEncSignal *signal = static_cast<AEncSignal *>(userData);
75     unique_lock<mutex> lock(signal->outMutex_);
76     signal->outQueue_.push(index);
77     signal->outBufferQueue_.push(data);
78     if (attr) {
79         cout << "OnOutputBufferAvailable received, index:" << index << ", attr->size:" << attr->size
80              << ", attr->flags:" << attr->flags << ", pts " << attr->pts << endl;
81         signal->attrQueue_.push(*attr);
82     } else {
83         cout << "OnOutputBufferAvailable error, attr is nullptr!" << endl;
84     }
85     signal->outCond_.notify_all();
86 }
87 
RunCase()88 void AEncG711muDemo::RunCase()
89 {
90     std::cout << "RunCase enter" << std::endl;
91     DEMO_CHECK_AND_RETURN_LOG(CreateEnc() == AVCS_ERR_OK, "Fatal: CreateEnc fail");
92 
93     OH_AVFormat *format = OH_AVFormat_Create();
94     OH_AVFormat_SetIntValue(format, MediaDescriptionKey::MD_KEY_CHANNEL_COUNT.data(), CHANNEL_COUNT);
95     OH_AVFormat_SetIntValue(format, MediaDescriptionKey::MD_KEY_SAMPLE_RATE.data(), SAMPLE_RATE);
96     OH_AVFormat_SetIntValue(format, MediaDescriptionKey::MD_KEY_AUDIO_SAMPLE_FORMAT.data(), SAMPLE_FORMAT);
97 
98     DEMO_CHECK_AND_RETURN_LOG(Configure(format) == AVCS_ERR_OK, "Fatal: Configure fail");
99 
100     auto fmt = OH_AudioEncoder_GetOutputDescription(audioEnc_);
101     int channels;
102     int sampleRate;
103     int64_t bitRate;
104     int sampleFormat;
105     int64_t channelLayout;
106     int frameSize;
107     OH_AVFormat_GetIntValue(fmt, MediaDescriptionKey::MD_KEY_CHANNEL_COUNT.data(), &channels);
108     OH_AVFormat_GetIntValue(fmt, MediaDescriptionKey::MD_KEY_SAMPLE_RATE.data(), &sampleRate);
109     OH_AVFormat_GetLongValue(fmt, MediaDescriptionKey::MD_KEY_BITRATE.data(), &bitRate);
110     OH_AVFormat_GetIntValue(fmt, MediaDescriptionKey::MD_KEY_AUDIO_SAMPLE_FORMAT.data(), &sampleFormat);
111     OH_AVFormat_GetLongValue(fmt, MediaDescriptionKey::MD_KEY_CHANNEL_LAYOUT.data(), &channelLayout);
112     OH_AVFormat_GetIntValue(fmt, MediaDescriptionKey::MD_KEY_AUDIO_SAMPLES_PER_FRAME.data(), &frameSize);
113     std::cout << "GetOutputDescription " << "channels: " << channels
114               << ", sampleRate: " << sampleRate
115               << ", bitRate: " << bitRate
116               << ", sampleFormat: " << sampleFormat
117               << ", channelLayout: " << channelLayout
118               << ", frameSize: " << frameSize << std::endl;
119 
120     DEMO_CHECK_AND_RETURN_LOG(Start() == AVCS_ERR_OK, "Fatal: Start fail");
121     auto start = chrono::steady_clock::now();
122 
123     {
124         unique_lock<mutex> lock(signal_->startMutex_);
125         signal_->startCond_.wait(lock, [this]() { return (!(isRunning_.load())); });
126     }
127 
128     auto end = chrono::steady_clock::now();
129     std::cout << "Encode finished, time = " << std::chrono::duration_cast<chrono::milliseconds>(end - start).count()
130               << " ms" << std::endl;
131     DEMO_CHECK_AND_RETURN_LOG(Stop() == AVCS_ERR_OK, "Fatal: Stop fail");
132     DEMO_CHECK_AND_RETURN_LOG(Release() == AVCS_ERR_OK, "Fatal: Release fail");
133     OH_AVFormat_Destroy(format);
134 }
135 
AEncG711muDemo()136 AEncG711muDemo::AEncG711muDemo()
137     : isRunning_(false),
138       inputFile_(std::make_unique<std::ifstream>(INPUT_FILE_PATH, std::ios::binary)),
139       outputFile_(std::make_unique<std::ofstream>(OUTPUT_FILE_PATH, std::ios::binary)),
140       audioEnc_(nullptr),
141       signal_(nullptr),
142       frameCount_(0)
143 {
144 }
145 
~AEncG711muDemo()146 AEncG711muDemo::~AEncG711muDemo()
147 {
148     if (signal_) {
149         delete signal_;
150         signal_ = nullptr;
151     }
152 }
153 
CreateEnc()154 int32_t AEncG711muDemo::CreateEnc()
155 {
156     audioEnc_ = OH_AudioEncoder_CreateByName((AVCodecCodecName::AUDIO_ENCODER_G711MU_NAME).data());
157     DEMO_CHECK_AND_RETURN_RET_LOG(audioEnc_ != nullptr, AVCS_ERR_UNKNOWN, "Fatal: CreateByName fail");
158 
159     signal_ = new AEncSignal();
160     DEMO_CHECK_AND_RETURN_RET_LOG(signal_ != nullptr, AVCS_ERR_UNKNOWN, "Fatal: No memory");
161 
162     cb_ = {&OnError, &OnOutputFormatChanged, &OnInputBufferAvailable, &OnOutputBufferAvailable};
163     int32_t ret = OH_AudioEncoder_SetCallback(audioEnc_, cb_, signal_);
164     DEMO_CHECK_AND_RETURN_RET_LOG(ret == AVCS_ERR_OK, AVCS_ERR_UNKNOWN, "Fatal: SetCallback fail");
165 
166     return AVCS_ERR_OK;
167 }
168 
Configure(OH_AVFormat * format)169 int32_t AEncG711muDemo::Configure(OH_AVFormat *format)
170 {
171     return OH_AudioEncoder_Configure(audioEnc_, format);
172 }
173 
Start()174 int32_t AEncG711muDemo::Start()
175 {
176     isRunning_.store(true);
177 
178     inputLoop_ = make_unique<thread>(&AEncG711muDemo::InputFunc, this);
179     DEMO_CHECK_AND_RETURN_RET_LOG(inputLoop_ != nullptr, AVCS_ERR_UNKNOWN, "Fatal: No memory");
180 
181     outputLoop_ = make_unique<thread>(&AEncG711muDemo::OutputFunc, this);
182     DEMO_CHECK_AND_RETURN_RET_LOG(outputLoop_ != nullptr, AVCS_ERR_UNKNOWN, "Fatal: No memory");
183 
184     return OH_AudioEncoder_Start(audioEnc_);
185 }
186 
Stop()187 int32_t AEncG711muDemo::Stop()
188 {
189     isRunning_.store(false);
190 
191     if (inputLoop_ != nullptr && inputLoop_->joinable()) {
192         {
193             unique_lock<mutex> lock(signal_->inMutex_);
194             signal_->inCond_.notify_all();
195         }
196         inputLoop_->join();
197         inputLoop_ = nullptr;
198         while (!signal_->inQueue_.empty()) {
199             signal_->inQueue_.pop();
200         }
201         while (!signal_->inBufferQueue_.empty()) {
202             signal_->inBufferQueue_.pop();
203         }
204     }
205 
206     if (outputLoop_ != nullptr && outputLoop_->joinable()) {
207         {
208             unique_lock<mutex> lock(signal_->outMutex_);
209             signal_->outCond_.notify_all();
210         }
211         outputLoop_->join();
212         outputLoop_ = nullptr;
213         while (!signal_->outQueue_.empty()) {
214             signal_->outQueue_.pop();
215         }
216         while (!signal_->attrQueue_.empty()) {
217             signal_->attrQueue_.pop();
218         }
219         while (!signal_->outBufferQueue_.empty()) {
220             signal_->outBufferQueue_.pop();
221         }
222     }
223 
224     return OH_AudioEncoder_Stop(audioEnc_);
225 }
226 
Flush()227 int32_t AEncG711muDemo::Flush()
228 {
229     return 0;
230 }
231 
Reset()232 int32_t AEncG711muDemo::Reset()
233 {
234     return 0;
235 }
236 
Release()237 int32_t AEncG711muDemo::Release()
238 {
239     return 0;
240 }
241 
HandleEOS(const uint32_t & index)242 void AEncG711muDemo::HandleEOS(const uint32_t &index)
243 {
244     OH_AVCodecBufferAttr info;
245     info.size = 0;
246     info.offset = 0;
247     info.pts = 0;
248     info.flags = AVCODEC_BUFFER_FLAGS_EOS;
249     OH_AudioEncoder_PushInputData(audioEnc_, index, info);
250     std::cout << "end buffer\n";
251     signal_->inQueue_.pop();
252     signal_->inBufferQueue_.pop();
253 }
254 
InputFunc()255 void AEncG711muDemo::InputFunc()
256 {
257     DEMO_CHECK_AND_RETURN_LOG(inputFile_ != nullptr && inputFile_->is_open(), "Fatal: open file fail");
258     while (isRunning_.load()) {
259         unique_lock<mutex> lock(signal_->inMutex_);
260         signal_->inCond_.wait(lock, [this]() { return (signal_->inQueue_.size() > 0 || !isRunning_.load()); });
261         if (!isRunning_.load()) {
262             break;
263         }
264         uint32_t index = signal_->inQueue_.front();
265         auto buffer = signal_->inBufferQueue_.front();
266         DEMO_CHECK_AND_BREAK_LOG(buffer != nullptr, "Fatal: GetInputBuffer fail");
267 
268         if (!inputFile_->eof()) {
269             inputFile_->read(reinterpret_cast<char *>(OH_AVMemory_GetAddr(buffer)), INPUT_FRAME_BYTES);
270             if (inputFile_->gcount() == 0) {
271                 HandleEOS(index);
272                 break;
273             }
274         } else {
275             HandleEOS(index);
276             break;
277         }
278         DEMO_CHECK_AND_BREAK_LOG(buffer != nullptr, "Fatal: GetInputBuffer fail");
279         OH_AVCodecBufferAttr info;
280         info.size = INPUT_FRAME_BYTES;
281         info.offset = 0;
282 
283         int32_t ret = AVCS_ERR_OK;
284         if (isFirstFrame_) {
285             info.flags = AVCODEC_BUFFER_FLAGS_CODEC_DATA;
286             ret = OH_AudioEncoder_PushInputData(audioEnc_, index, info);
287             isFirstFrame_ = false;
288         } else {
289             info.flags = AVCODEC_BUFFER_FLAGS_NONE;
290             ret = OH_AudioEncoder_PushInputData(audioEnc_, index, info);
291         }
292         timeStamp_ += FRAME_DURATION_US;
293         signal_->inQueue_.pop();
294         signal_->inBufferQueue_.pop();
295         frameCount_++;
296         if (ret != AVCS_ERR_OK) {
297             cout << "Fatal error, exit" << endl;
298             break;
299         }
300     }
301     inputFile_->close();
302 }
303 
OutputFunc()304 void AEncG711muDemo::OutputFunc()
305 {
306     DEMO_CHECK_AND_RETURN_LOG(outputFile_ != nullptr && outputFile_->is_open(), "Fatal: open output file fail");
307     while (isRunning_.load()) {
308         unique_lock<mutex> lock(signal_->outMutex_);
309         signal_->outCond_.wait(lock, [this]() { return (signal_->outQueue_.size() > 0 || !isRunning_.load()); });
310 
311         if (!isRunning_.load()) {
312             cout << "wait to stop, exit" << endl;
313             break;
314         }
315 
316         uint32_t index = signal_->outQueue_.front();
317 
318         OH_AVCodecBufferAttr attr = signal_->attrQueue_.front();
319         OH_AVMemory *data = signal_->outBufferQueue_.front();
320         if (data != nullptr) {
321             cout << "OutputFunc write file,buffer index" << index << ", data size = :" << attr.size << endl;
322             outputFile_->write(reinterpret_cast<char *>(OH_AVMemory_GetAddr(data)), attr.size);
323         }
324         if (attr.flags == AVCODEC_BUFFER_FLAGS_EOS || attr.size == 0) {
325             cout << "encode eos" << endl;
326             isRunning_.store(false);
327             signal_->startCond_.notify_all();
328         }
329 
330         signal_->outBufferQueue_.pop();
331         signal_->attrQueue_.pop();
332         signal_->outQueue_.pop();
333         if (OH_AudioEncoder_FreeOutputData(audioEnc_, index) != AV_ERR_OK) {
334             cout << "Fatal: FreeOutputData fail" << endl;
335             break;
336         }
337         if (attr.flags == AVCODEC_BUFFER_FLAGS_EOS) {
338             cout << "decode eos" << endl;
339             isRunning_.store(false);
340             signal_->startCond_.notify_all();
341         }
342     }
343     outputFile_->close();
344 }
345