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