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