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_aac_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 "ffmpeg_converter.h"
28
29 using namespace OHOS;
30 using namespace OHOS::MediaAVCodec;
31 using namespace OHOS::MediaAVCodec::AudioAacDemo;
32 using namespace std;
33 namespace {
34 constexpr uint32_t CHANNEL_COUNT = 2;
35 constexpr uint32_t SAMPLE_RATE = 44100;
36 constexpr uint32_t FRAME_DURATION_US = 33000;
37 constexpr int32_t SAMPLE_FORMAT = AudioSampleFormat::SAMPLE_F32LE;
38 constexpr int32_t INPUT_FRAME_BYTES = 2 * 1024 * 4;
39
40 constexpr string_view INPUT_FILE_PATH = "/data/test/media/aac_2c_44100hz_199k.pcm";
41 constexpr string_view OUTPUT_FILE_PATH = "/data/test/media/encode2.aac";
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 AEncAacDemo::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
AEncAacDemo()136 AEncAacDemo::AEncAacDemo() : isRunning_(false), audioEnc_(nullptr), signal_(nullptr), frameCount_(0)
137 {
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 }
141
~AEncAacDemo()142 AEncAacDemo::~AEncAacDemo()
143 {
144 if (signal_) {
145 delete signal_;
146 signal_ = nullptr;
147 }
148 }
149
CreateEnc()150 int32_t AEncAacDemo::CreateEnc()
151 {
152 audioEnc_ = OH_AudioEncoder_CreateByName((AVCodecCodecName::AUDIO_ENCODER_AAC_NAME).data());
153 DEMO_CHECK_AND_RETURN_RET_LOG(audioEnc_ != nullptr, AVCS_ERR_UNKNOWN, "Fatal: CreateByName fail");
154
155 signal_ = new AEncSignal();
156 DEMO_CHECK_AND_RETURN_RET_LOG(signal_ != nullptr, AVCS_ERR_UNKNOWN, "Fatal: No memory");
157
158 cb_ = {&OnError, &OnOutputFormatChanged, &OnInputBufferAvailable, &OnOutputBufferAvailable};
159 int32_t ret = OH_AudioEncoder_SetCallback(audioEnc_, cb_, signal_);
160 DEMO_CHECK_AND_RETURN_RET_LOG(ret == AVCS_ERR_OK, AVCS_ERR_UNKNOWN, "Fatal: SetCallback fail");
161
162 return AVCS_ERR_OK;
163 }
164
Configure(OH_AVFormat * format)165 int32_t AEncAacDemo::Configure(OH_AVFormat *format)
166 {
167 return OH_AudioEncoder_Configure(audioEnc_, format);
168 }
169
Start()170 int32_t AEncAacDemo::Start()
171 {
172 isRunning_.store(true);
173
174 inputLoop_ = make_unique<thread>(&AEncAacDemo::InputFunc, this);
175 DEMO_CHECK_AND_RETURN_RET_LOG(inputLoop_ != nullptr, AVCS_ERR_UNKNOWN, "Fatal: No memory");
176
177 outputLoop_ = make_unique<thread>(&AEncAacDemo::OutputFunc, this);
178 DEMO_CHECK_AND_RETURN_RET_LOG(outputLoop_ != nullptr, AVCS_ERR_UNKNOWN, "Fatal: No memory");
179
180 return OH_AudioEncoder_Start(audioEnc_);
181 }
182
Stop()183 int32_t AEncAacDemo::Stop()
184 {
185 isRunning_.store(false);
186
187 if (inputLoop_ != nullptr && inputLoop_->joinable()) {
188 {
189 unique_lock<mutex> lock(signal_->inMutex_);
190 signal_->inCond_.notify_all();
191 }
192 inputLoop_->join();
193 inputLoop_ = nullptr;
194 while (!signal_->inQueue_.empty()) {
195 signal_->inQueue_.pop();
196 }
197 while (!signal_->inBufferQueue_.empty()) {
198 signal_->inBufferQueue_.pop();
199 }
200 }
201
202 if (outputLoop_ != nullptr && outputLoop_->joinable()) {
203 {
204 unique_lock<mutex> lock(signal_->outMutex_);
205 signal_->outCond_.notify_all();
206 }
207 outputLoop_->join();
208 outputLoop_ = nullptr;
209 while (!signal_->outQueue_.empty()) {
210 signal_->outQueue_.pop();
211 }
212 while (!signal_->attrQueue_.empty()) {
213 signal_->attrQueue_.pop();
214 }
215 while (!signal_->outBufferQueue_.empty()) {
216 signal_->outBufferQueue_.pop();
217 }
218 }
219
220 return OH_AudioEncoder_Stop(audioEnc_);
221 }
222
Flush()223 int32_t AEncAacDemo::Flush()
224 {
225 if (inputLoop_ != nullptr && inputLoop_->joinable()) {
226 {
227 unique_lock<mutex> lock(signal_->inMutex_);
228 signal_->inCond_.notify_all();
229 }
230 inputLoop_->join();
231 inputLoop_ = nullptr;
232 while (!signal_->inQueue_.empty()) {
233 signal_->inQueue_.pop();
234 }
235 while (!signal_->inBufferQueue_.empty()) {
236 signal_->inBufferQueue_.pop();
237 }
238 std::cout << "clear input buffer!\n";
239 }
240
241 if (outputLoop_ != nullptr && outputLoop_->joinable()) {
242 {
243 unique_lock<mutex> lock(signal_->outMutex_);
244 signal_->outCond_.notify_all();
245 }
246 outputLoop_->join();
247 outputLoop_ = nullptr;
248 while (!signal_->outQueue_.empty()) {
249 signal_->outQueue_.pop();
250 }
251 while (!signal_->attrQueue_.empty()) {
252 signal_->attrQueue_.pop();
253 }
254 while (!signal_->outBufferQueue_.empty()) {
255 signal_->outBufferQueue_.pop();
256 }
257 std::cout << "clear output buffer!\n";
258 }
259 return OH_AudioEncoder_Flush(audioEnc_);
260 }
261
Reset()262 int32_t AEncAacDemo::Reset()
263 {
264 return OH_AudioEncoder_Reset(audioEnc_);
265 }
266
Release()267 int32_t AEncAacDemo::Release()
268 {
269 return OH_AudioEncoder_Destroy(audioEnc_);
270 }
271
HandleEOS(const uint32_t & index)272 void AEncAacDemo::HandleEOS(const uint32_t &index)
273 {
274 OH_AVCodecBufferAttr info;
275 info.size = 0;
276 info.offset = 0;
277 info.pts = 0;
278 info.flags = AVCODEC_BUFFER_FLAGS_EOS;
279 OH_AudioEncoder_PushInputData(audioEnc_, index, info);
280 std::cout << "end buffer\n";
281 signal_->inQueue_.pop();
282 signal_->inBufferQueue_.pop();
283 }
284
InputFunc()285 void AEncAacDemo::InputFunc()
286 {
287 DEMO_CHECK_AND_RETURN_LOG(inputFile_ != nullptr && inputFile_->is_open(), "Fatal: open file fail");
288 while (true) {
289 if (!isRunning_.load()) {
290 break;
291 }
292 unique_lock<mutex> lock(signal_->inMutex_);
293 signal_->inCond_.wait(lock, [this]() { return (signal_->inQueue_.size() > 0 || !isRunning_.load()); });
294 if (!isRunning_.load()) {
295 break;
296 }
297 uint32_t index = signal_->inQueue_.front();
298 auto buffer = signal_->inBufferQueue_.front();
299 DEMO_CHECK_AND_BREAK_LOG(buffer != nullptr, "Fatal: GetInputBuffer fail");
300
301 if (!inputFile_->eof()) {
302 inputFile_->read((char *)OH_AVMemory_GetAddr(buffer), INPUT_FRAME_BYTES);
303 } else {
304 HandleEOS(index);
305 break;
306 }
307 DEMO_CHECK_AND_BREAK_LOG(buffer != nullptr, "Fatal: GetInputBuffer fail");
308 OH_AVCodecBufferAttr info;
309 info.size = INPUT_FRAME_BYTES;
310 info.offset = 0;
311
312 int32_t ret = AVCS_ERR_OK;
313 if (isFirstFrame_) {
314 info.flags = AVCODEC_BUFFER_FLAGS_CODEC_DATA;
315 ret = OH_AudioEncoder_PushInputData(audioEnc_, index, info);
316 isFirstFrame_ = false;
317 } else {
318 info.flags = AVCODEC_BUFFER_FLAGS_NONE;
319 ret = OH_AudioEncoder_PushInputData(audioEnc_, index, info);
320 }
321 timeStamp_ += FRAME_DURATION_US;
322 signal_->inQueue_.pop();
323 signal_->inBufferQueue_.pop();
324 frameCount_++;
325 if (ret != AVCS_ERR_OK) {
326 cout << "Fatal error, exit" << endl;
327 break;
328 }
329 }
330 inputFile_->close();
331 }
332
OutputFunc()333 void AEncAacDemo::OutputFunc()
334 {
335 DEMO_CHECK_AND_RETURN_LOG(outputFile_ != nullptr && outputFile_->is_open(), "Fatal: open output file fail");
336 while (true) {
337 if (!isRunning_.load()) {
338 cout << "stop, exit" << endl;
339 break;
340 }
341
342 unique_lock<mutex> lock(signal_->outMutex_);
343 signal_->outCond_.wait(lock, [this]() { return (signal_->outQueue_.size() > 0 || !isRunning_.load()); });
344
345 if (!isRunning_.load()) {
346 cout << "wait to stop, exit" << endl;
347 break;
348 }
349
350 uint32_t index = signal_->outQueue_.front();
351
352 OH_AVCodecBufferAttr attr = signal_->attrQueue_.front();
353 OH_AVMemory *data = signal_->outBufferQueue_.front();
354 if (data != nullptr) {
355 cout << "OutputFunc write file,buffer index" << index << ", data size = :" << attr.size << endl;
356 outputFile_->write(reinterpret_cast<char *>(OH_AVMemory_GetAddr(data)), attr.size);
357 }
358 if (attr.flags == AVCODEC_BUFFER_FLAGS_EOS || attr.size == 0) {
359 cout << "encode eos" << endl;
360 isRunning_.store(false);
361 signal_->startCond_.notify_all();
362 }
363
364 signal_->outBufferQueue_.pop();
365 signal_->attrQueue_.pop();
366 signal_->outQueue_.pop();
367 if (OH_AudioEncoder_FreeOutputData(audioEnc_, index) != AV_ERR_OK) {
368 cout << "Fatal: FreeOutputData fail" << endl;
369 break;
370 }
371 if (attr.flags == AVCODEC_BUFFER_FLAGS_EOS) {
372 cout << "decode eos" << endl;
373 isRunning_.store(false);
374 signal_->startCond_.notify_all();
375 }
376 }
377 outputFile_->close();
378 }
379