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