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