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_avbuffer_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 "native_avmemory.h"
28 #include "native_avbuffer.h"
29 #include "ffmpeg_converter.h"
30 
31 using namespace OHOS;
32 using namespace OHOS::MediaAVCodec;
33 using namespace OHOS::MediaAVCodec::AudioAacEncDemo;
34 using namespace std;
35 namespace {
36 constexpr uint32_t CHANNEL_COUNT = 2;
37 constexpr uint32_t SAMPLE_RATE = 44100;
38 constexpr uint32_t FRAME_DURATION_US = 33000;
39 constexpr int32_t SAMPLE_FORMAT = AudioSampleFormat::SAMPLE_S32LE;
40 constexpr int32_t INPUT_FRAME_BYTES = 2 * 1024 * 4;
41 
42 constexpr string_view INPUT_FILE_PATH = "/data/test/media/aac_2c_44100hz_199k.pcm";
43 constexpr string_view OUTPUT_FILE_PATH = "/data/test/media/encode2.aac";
44 } // namespace
45 
OnError(OH_AVCodec * codec,int32_t errorCode,void * userData)46 static void OnError(OH_AVCodec *codec, int32_t errorCode, void *userData)
47 {
48     (void)codec;
49     (void)errorCode;
50     (void)userData;
51     cout << "Error received, errorCode:" << errorCode << endl;
52 }
53 
OnOutputFormatChanged(OH_AVCodec * codec,OH_AVFormat * format,void * userData)54 static void OnOutputFormatChanged(OH_AVCodec *codec, OH_AVFormat *format, void *userData)
55 {
56     (void)codec;
57     (void)format;
58     (void)userData;
59     cout << "OnOutputFormatChanged received" << endl;
60 }
61 
OnInputBufferAvailable(OH_AVCodec * codec,uint32_t index,OH_AVBuffer * buffer,void * userData)62 static void OnInputBufferAvailable(OH_AVCodec *codec, uint32_t index, OH_AVBuffer *buffer, void *userData)
63 {
64     (void)codec;
65     AEncSignal *signal = static_cast<AEncSignal *>(userData);
66     unique_lock<mutex> lock(signal->inMutex_);
67     signal->inQueue_.push(index);
68     signal->inBufferQueue_.push(buffer);
69     signal->inCond_.notify_all();
70 }
71 
OnOutputBufferAvailable(OH_AVCodec * codec,uint32_t index,OH_AVBuffer * buffer,void * userData)72 static void OnOutputBufferAvailable(OH_AVCodec *codec, uint32_t index, OH_AVBuffer *buffer, void *userData)
73 {
74     (void)codec;
75     AEncSignal *signal = static_cast<AEncSignal *>(userData);
76     unique_lock<mutex> lock(signal->outMutex_);
77     signal->outQueue_.push(index);
78     signal->outBufferQueue_.push(buffer);
79     if (buffer) {
80         cout << "OnOutputBufferAvailable received, index:" << index << ", size:" << buffer->buffer_->memory_->GetSize()
81              << ", flags:" << buffer->buffer_->flag_ << ", pts: " << buffer->buffer_->pts_ << endl;
82     } else {
83         cout << "OnOutputBufferAvailable error, attr is nullptr!" << endl;
84     }
85     signal->outCond_.notify_all();
86 }
87 
RunCase()88 void AudioBufferAacEncDemo::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_AudioCodec_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 "
114               << "channels: " << channels << ", sampleRate: " << sampleRate << ", bitRate: " << bitRate
115               << ", sampleFormat: " << sampleFormat << ", channelLayout: " << channelLayout
116               << ", frameSize: " << frameSize << std::endl;
117 
118     DEMO_CHECK_AND_RETURN_LOG(Start() == AVCS_ERR_OK, "Fatal: Start fail");
119     auto start = chrono::steady_clock::now();
120 
121     {
122         unique_lock<mutex> lock(signal_->startMutex_);
123         signal_->startCond_.wait(lock, [this]() { return (!(isRunning_.load())); });
124     }
125 
126     auto end = chrono::steady_clock::now();
127     std::cout << "Encode finished, time = " << std::chrono::duration_cast<chrono::milliseconds>(end - start).count()
128               << " ms" << std::endl;
129     DEMO_CHECK_AND_RETURN_LOG(Stop() == AVCS_ERR_OK, "Fatal: Stop fail");
130     DEMO_CHECK_AND_RETURN_LOG(Release() == AVCS_ERR_OK, "Fatal: Release fail");
131     OH_AVFormat_Destroy(format);
132 }
133 
GetFileSize(const std::string & filePath)134 int32_t AudioBufferAacEncDemo::GetFileSize(const std::string &filePath)
135 {
136     std::ifstream file(filePath, std::ios::binary | std::ios::ate);
137     if (!file) {
138         std::cerr << "Failed to open file: " << filePath << std::endl;
139         return -1;
140     }
141 
142     std::streampos fileSize = file.tellg(); // 获取文件大小
143     file.close();
144 
145     return (int32_t)fileSize;
146 }
147 
AudioBufferAacEncDemo()148 AudioBufferAacEncDemo::AudioBufferAacEncDemo() : isRunning_(false), audioEnc_(nullptr), signal_(nullptr), frameCount_(0)
149 {
150     fileSize_ = GetFileSize(INPUT_FILE_PATH.data());
151     inputFile_ = std::make_unique<std::ifstream>(INPUT_FILE_PATH, std::ios::binary);
152     outputFile_ = std::make_unique<std::ofstream>(OUTPUT_FILE_PATH, std::ios::binary);
153 }
154 
~AudioBufferAacEncDemo()155 AudioBufferAacEncDemo::~AudioBufferAacEncDemo()
156 {
157     if (signal_) {
158         delete signal_;
159         signal_ = nullptr;
160     }
161 }
162 
CreateEnc()163 int32_t AudioBufferAacEncDemo::CreateEnc()
164 {
165     audioEnc_ = OH_AudioCodec_CreateByName((AVCodecCodecName::AUDIO_ENCODER_AAC_NAME).data());
166     DEMO_CHECK_AND_RETURN_RET_LOG(audioEnc_ != nullptr, AVCS_ERR_UNKNOWN, "Fatal: CreateByName fail");
167 
168     signal_ = new AEncSignal();
169     DEMO_CHECK_AND_RETURN_RET_LOG(signal_ != nullptr, AVCS_ERR_UNKNOWN, "Fatal: No memory");
170 
171     cb_ = {&OnError, &OnOutputFormatChanged, &OnInputBufferAvailable, &OnOutputBufferAvailable};
172     int32_t ret = OH_AudioCodec_RegisterCallback(audioEnc_, cb_, signal_);
173     DEMO_CHECK_AND_RETURN_RET_LOG(ret == AVCS_ERR_OK, AVCS_ERR_UNKNOWN, "Fatal: SetCallback fail");
174 
175     return AVCS_ERR_OK;
176 }
177 
Configure(OH_AVFormat * format)178 int32_t AudioBufferAacEncDemo::Configure(OH_AVFormat *format)
179 {
180     return OH_AudioCodec_Configure(audioEnc_, format);
181 }
182 
Start()183 int32_t AudioBufferAacEncDemo::Start()
184 {
185     isRunning_.store(true);
186 
187     inputLoop_ = make_unique<thread>(&AudioBufferAacEncDemo::InputFunc, this);
188     DEMO_CHECK_AND_RETURN_RET_LOG(inputLoop_ != nullptr, AVCS_ERR_UNKNOWN, "Fatal: No memory");
189 
190     outputLoop_ = make_unique<thread>(&AudioBufferAacEncDemo::OutputFunc, this);
191     DEMO_CHECK_AND_RETURN_RET_LOG(outputLoop_ != nullptr, AVCS_ERR_UNKNOWN, "Fatal: No memory");
192 
193     return OH_AudioCodec_Start(audioEnc_);
194 }
195 
Stop()196 int32_t AudioBufferAacEncDemo::Stop()
197 {
198     isRunning_.store(false);
199 
200     if (inputLoop_ != nullptr && inputLoop_->joinable()) {
201         {
202             unique_lock<mutex> lock(signal_->inMutex_);
203             signal_->inCond_.notify_all();
204         }
205         inputLoop_->join();
206         inputLoop_ = nullptr;
207         while (!signal_->inQueue_.empty()) {
208             signal_->inQueue_.pop();
209         }
210         while (!signal_->inBufferQueue_.empty()) {
211             signal_->inBufferQueue_.pop();
212         }
213     }
214 
215     if (outputLoop_ != nullptr && outputLoop_->joinable()) {
216         {
217             unique_lock<mutex> lock(signal_->outMutex_);
218             signal_->outCond_.notify_all();
219         }
220         outputLoop_->join();
221         outputLoop_ = nullptr;
222         while (!signal_->outQueue_.empty()) {
223             signal_->outQueue_.pop();
224         }
225         while (!signal_->outBufferQueue_.empty()) {
226             signal_->outBufferQueue_.pop();
227         }
228     }
229 
230     return OH_AudioCodec_Stop(audioEnc_);
231 }
232 
Flush()233 int32_t AudioBufferAacEncDemo::Flush()
234 {
235     if (inputLoop_ != nullptr && inputLoop_->joinable()) {
236         {
237             unique_lock<mutex> lock(signal_->inMutex_);
238             signal_->inCond_.notify_all();
239         }
240         inputLoop_->join();
241         inputLoop_ = nullptr;
242         while (!signal_->inQueue_.empty()) {
243             signal_->inQueue_.pop();
244         }
245         while (!signal_->inBufferQueue_.empty()) {
246             signal_->inBufferQueue_.pop();
247         }
248         std::cout << "clear input buffer!\n";
249     }
250 
251     if (outputLoop_ != nullptr && outputLoop_->joinable()) {
252         {
253             unique_lock<mutex> lock(signal_->outMutex_);
254             signal_->outCond_.notify_all();
255         }
256         outputLoop_->join();
257         outputLoop_ = nullptr;
258         while (!signal_->outQueue_.empty()) {
259             signal_->outQueue_.pop();
260         }
261         while (!signal_->outBufferQueue_.empty()) {
262             signal_->outBufferQueue_.pop();
263         }
264         std::cout << "clear output buffer!\n";
265     }
266     return OH_AudioCodec_Flush(audioEnc_);
267 }
268 
Reset()269 int32_t AudioBufferAacEncDemo::Reset()
270 {
271     return OH_AudioCodec_Reset(audioEnc_);
272 }
273 
Release()274 int32_t AudioBufferAacEncDemo::Release()
275 {
276     return OH_AudioCodec_Destroy(audioEnc_);
277 }
278 
HandleEOS(const uint32_t & index)279 void AudioBufferAacEncDemo::HandleEOS(const uint32_t &index)
280 {
281     OH_AudioCodec_PushInputBuffer(audioEnc_, index);
282     std::cout << "end buffer\n";
283     signal_->inQueue_.pop();
284     signal_->inBufferQueue_.pop();
285 }
286 
InputFunc()287 void AudioBufferAacEncDemo::InputFunc()
288 {
289     DEMO_CHECK_AND_RETURN_LOG(inputFile_ != nullptr && inputFile_->is_open(), "Fatal: open file fail");
290     int32_t sumReadSize = 0;
291     while (isRunning_.load()) {
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         if (!inputFile_->eof()) {
301             inputFile_->read(reinterpret_cast<char *>(OH_AVBuffer_GetAddr(buffer)), INPUT_FRAME_BYTES);
302             buffer->buffer_->memory_->SetSize(INPUT_FRAME_BYTES);
303             sumReadSize += INPUT_FRAME_BYTES;
304         } else {
305             buffer->buffer_->memory_->SetSize(1);
306             buffer->buffer_->flag_ = AVCODEC_BUFFER_FLAGS_EOS;
307             HandleEOS(index);
308             sumReadSize += 0;
309             break;
310         }
311         DEMO_CHECK_AND_BREAK_LOG(buffer != nullptr, "Fatal: GetInputBuffer fail");
312 
313         cout << "InputFunc, INPUT_FRAME_BYTES:" << INPUT_FRAME_BYTES << " flag:" << buffer->buffer_->flag_
314              << " sumReadSize:" << sumReadSize << " fileSize_:" << fileSize_
315              << " process:" << 100 * sumReadSize / fileSize_ << "%" << endl;   // 100
316 
317         int32_t ret = AVCS_ERR_OK;
318         if (isFirstFrame_) {
319             buffer->buffer_->flag_ = AVCODEC_BUFFER_FLAGS_CODEC_DATA;
320             ret = OH_AudioCodec_PushInputBuffer(audioEnc_, index);
321             isFirstFrame_ = false;
322         } else {
323             buffer->buffer_->flag_ = AVCODEC_BUFFER_FLAGS_NONE;
324             ret = OH_AudioCodec_PushInputBuffer(audioEnc_, index);
325         }
326         timeStamp_ += FRAME_DURATION_US;
327         signal_->inQueue_.pop();
328         signal_->inBufferQueue_.pop();
329         frameCount_++;
330         if (ret != AVCS_ERR_OK) {
331             cout << "Fatal error, exit" << endl;
332             break;
333         }
334     }
335     cout << "stop, exit" << endl;
336     inputFile_->close();
337 }
338 
OutputFunc()339 void AudioBufferAacEncDemo::OutputFunc()
340 {
341     DEMO_CHECK_AND_RETURN_LOG(outputFile_ != nullptr && outputFile_->is_open(), "Fatal: open output file fail");
342     while (isRunning_.load()) {
343         unique_lock<mutex> lock(signal_->outMutex_);
344         signal_->outCond_.wait(lock, [this]() { return (signal_->outQueue_.size() > 0 || !isRunning_.load()); });
345 
346         if (!isRunning_.load()) {
347             cout << "wait to stop, exit" << endl;
348             break;
349         }
350 
351         uint32_t index = signal_->outQueue_.front();
352         OH_AVBuffer *avBuffer = signal_->outBufferQueue_.front();
353         if (avBuffer == nullptr) {
354             cout << "OutputFunc OH_AVBuffer is nullptr" << endl;
355             continue;
356         }
357         if (avBuffer != nullptr) {
358             cout << "OutputFunc write file,buffer index:" << index
359                  << ", data size:" << avBuffer->buffer_->memory_->GetSize() << endl;
360             outputFile_->write(reinterpret_cast<char *>(OH_AVBuffer_GetAddr(avBuffer)),
361                                avBuffer->buffer_->memory_->GetSize());
362         }
363         if (avBuffer != nullptr &&
364             (avBuffer->buffer_->flag_ == AVCODEC_BUFFER_FLAGS_EOS || avBuffer->buffer_->memory_->GetSize() == 0)) {
365             cout << "encode eos" << endl;
366             isRunning_.store(false);
367             signal_->startCond_.notify_all();
368         }
369 
370         signal_->outBufferQueue_.pop();
371         signal_->outQueue_.pop();
372         if (OH_AudioCodec_FreeOutputBuffer(audioEnc_, index) != AV_ERR_OK) {
373             cout << "Fatal: FreeOutputData fail" << endl;
374             break;
375         }
376         if (avBuffer->buffer_->flag_ == AVCODEC_BUFFER_FLAGS_EOS) {
377             cout << "decode eos" << endl;
378             isRunning_.store(false);
379             signal_->startCond_.notify_all();
380         }
381     }
382     cout << "stop, exit" << endl;
383     outputFile_->close();
384 }
385