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_decoder_demo.h"
17 #include <iostream>
18 #include <unistd.h>
19 #include <chrono>
20 #include "avcodec_codec_name.h"
21 #include "avcodec_common.h"
22 #include "avcodec_errors.h"
23 #include "demo_log.h"
24 #include "media_description.h"
25 #include "native_avcodec_base.h"
26 #include "native_avformat.h"
27 #include "securec.h"
28
29 using namespace OHOS;
30 using namespace OHOS::MediaAVCodec;
31 using namespace OHOS::MediaAVCodec::AudioDemo;
32 using namespace std;
33 namespace {
34 constexpr uint32_t CHANNEL_COUNT = 2;
35 constexpr uint32_t SAMPLE_RATE = 44100;
36 constexpr uint32_t DEFAULT_AAC_TYPE = 1;
37 constexpr int64_t BITS_RETE[TYPE_MAX] = {199000, 261000, 60000, 320000};
38 constexpr string_view INPUT_AAC_FILE_PATH = "/data/test/media/aac_2c_44100hz_199k.dat";
39 constexpr string_view OUTPUT_AAC_PCM_FILE_PATH = "/data/test/media/aac_2c_44100hz_199k.pcm";
40 constexpr string_view INPUT_FLAC_FILE_PATH = "/data/test/media/flac_2c_44100hz_261k.dat";
41 constexpr string_view OUTPUT_FLAC_PCM_FILE_PATH = "/data/test/media/flac_2c_44100hz_261k.pcm";
42 constexpr string_view INPUT_MP3_FILE_PATH = "/data/test/media/mp3_2c_44100hz_60k.dat";
43 constexpr string_view OUTPUT_MP3_PCM_FILE_PATH = "/data/test/media/mp3_2c_44100hz_60k.pcm";
44 constexpr string_view INPUT_VORBIS_FILE_PATH = "/data/test/media/vorbis_2c_44100hz_320k.dat";
45 constexpr string_view OUTPUT_VORBIS_PCM_FILE_PATH = "/data/test/media/vorbis_2c_44100hz_320k.pcm";
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_AVMemory * data,void * userData)64 static void OnInputBufferAvailable(OH_AVCodec *codec, uint32_t index, OH_AVMemory *data, void *userData)
65 {
66 (void)codec;
67 ADecSignal *signal = static_cast<ADecSignal *>(userData);
68 unique_lock<mutex> lock(signal->inMutex_);
69 signal->inQueue_.push(index);
70 signal->inBufferQueue_.push(data);
71 signal->inCond_.notify_all();
72 }
73
OnOutputBufferAvailable(OH_AVCodec * codec,uint32_t index,OH_AVMemory * data,OH_AVCodecBufferAttr * attr,void * userData)74 static void OnOutputBufferAvailable(OH_AVCodec *codec, uint32_t index, OH_AVMemory *data, OH_AVCodecBufferAttr *attr,
75 void *userData)
76 {
77 (void)codec;
78 ADecSignal *signal = static_cast<ADecSignal *>(userData);
79 unique_lock<mutex> lock(signal->outMutex_);
80 signal->outQueue_.push(index);
81 signal->outBufferQueue_.push(data);
82 if (attr) {
83 signal->attrQueue_.push(*attr);
84 } else {
85 cout << "OnOutputBufferAvailable error, attr is nullptr!" << endl;
86 }
87 signal->outCond_.notify_all();
88 }
89
InitFile(AudioFormatType audioType)90 bool ADecDemo::InitFile(AudioFormatType audioType)
91 {
92 if (audioType == TYPE_AAC) {
93 inputFile_.open(INPUT_AAC_FILE_PATH, std::ios::binary);
94 pcmOutputFile_.open(OUTPUT_AAC_PCM_FILE_PATH.data(), std::ios::out | std::ios::binary);
95 } else if (audioType == TYPE_FLAC) {
96 inputFile_.open(INPUT_FLAC_FILE_PATH, std::ios::binary);
97 pcmOutputFile_.open(OUTPUT_FLAC_PCM_FILE_PATH.data(), std::ios::out | std::ios::binary);
98 } else if (audioType == TYPE_MP3) {
99 inputFile_.open(INPUT_MP3_FILE_PATH, std::ios::binary);
100 pcmOutputFile_.open(OUTPUT_MP3_PCM_FILE_PATH.data(), std::ios::out | std::ios::binary);
101 } else if (audioType == TYPE_VORBIS) {
102 inputFile_.open(INPUT_VORBIS_FILE_PATH, std::ios::binary);
103 pcmOutputFile_.open(OUTPUT_VORBIS_PCM_FILE_PATH.data(), std::ios::out | std::ios::binary);
104 } else {
105 std::cout << "audio format type not support\n";
106 return false;
107 }
108 DEMO_CHECK_AND_RETURN_RET_LOG(inputFile_.is_open(), false, "Fatal: open input file failed");
109 DEMO_CHECK_AND_RETURN_RET_LOG(pcmOutputFile_.is_open(), false, "Fatal: open output file failed");
110 return true;
111 }
112
RunCase(AudioFormatType audioType)113 void ADecDemo::RunCase(AudioFormatType audioType)
114 {
115 DEMO_CHECK_AND_RETURN_LOG(InitFile(audioType), "Fatal: InitFile file failed");
116 audioType_ = audioType;
117 DEMO_CHECK_AND_RETURN_LOG(CreateDec() == AVCS_ERR_OK, "Fatal: CreateDec fail");
118
119 OH_AVFormat *format = OH_AVFormat_Create();
120 OH_AVFormat_SetIntValue(format, MediaDescriptionKey::MD_KEY_CHANNEL_COUNT.data(), CHANNEL_COUNT);
121 OH_AVFormat_SetIntValue(format, MediaDescriptionKey::MD_KEY_SAMPLE_RATE.data(), SAMPLE_RATE);
122 if (audioType == TYPE_AAC) {
123 OH_AVFormat_SetIntValue(format, MediaDescriptionKey::MD_KEY_AAC_IS_ADTS.data(), DEFAULT_AAC_TYPE);
124 OH_AVFormat_SetIntValue(format, MediaDescriptionKey::MD_KEY_AUDIO_SAMPLE_FORMAT.data(),
125 OH_BitsPerSample::SAMPLE_F32LE);
126 }
127 OH_AVFormat_SetLongValue(format, MediaDescriptionKey::MD_KEY_BITRATE.data(), BITS_RETE[audioType]);
128 if (audioType == TYPE_VORBIS) {
129 OH_AVFormat_SetIntValue(format, MediaDescriptionKey::MD_KEY_AUDIO_SAMPLE_FORMAT.data(),
130 OH_BitsPerSample::SAMPLE_F32LE);
131 // extradata for vorbis
132 int64_t extradataSize;
133 DEMO_CHECK_AND_RETURN_LOG(inputFile_.is_open(), "Fatal: file is not open");
134 inputFile_.read(reinterpret_cast<char *>(&extradataSize), sizeof(int64_t));
135 DEMO_CHECK_AND_RETURN_LOG(inputFile_.gcount() == sizeof(int64_t), "Fatal: read extradataSize bytes error");
136 if (extradataSize < 0) {
137 return;
138 }
139 char buffer[extradataSize];
140 inputFile_.read(buffer, extradataSize);
141 DEMO_CHECK_AND_RETURN_LOG(inputFile_.gcount() == extradataSize, "Fatal: read extradata bytes error");
142 OH_AVFormat_SetBuffer(format, MediaDescriptionKey::MD_KEY_CODEC_CONFIG.data(), (uint8_t *)buffer,
143 extradataSize);
144 }
145 DEMO_CHECK_AND_RETURN_LOG(Configure(format) == AVCS_ERR_OK, "Fatal: Configure fail");
146 DEMO_CHECK_AND_RETURN_LOG(Start() == AVCS_ERR_OK, "Fatal: Start fail");
147
148 auto start = chrono::steady_clock::now();
149
150 unique_lock<mutex> lock(signal_->startMutex_);
151 signal_->startCond_.wait(lock, [this]() { return (!(isRunning_.load())); });
152
153 auto end = chrono::steady_clock::now();
154 std::cout << "Encode finished, time = " << std::chrono::duration_cast<chrono::milliseconds>(end - start).count()
155 << " ms" << std::endl;
156
157 DEMO_CHECK_AND_RETURN_LOG(Stop() == AVCS_ERR_OK, "Fatal: Stop fail");
158 std::cout << "end stop!\n";
159 DEMO_CHECK_AND_RETURN_LOG(Release() == AVCS_ERR_OK, "Fatal: Release fail");
160 }
161
ADecDemo()162 ADecDemo::ADecDemo() : audioDec_(nullptr), signal_(nullptr), audioType_(TYPE_AAC) {}
163
~ADecDemo()164 ADecDemo::~ADecDemo()
165 {
166 if (signal_) {
167 delete signal_;
168 signal_ = nullptr;
169 }
170 if (inputFile_.is_open()) {
171 inputFile_.close();
172 }
173 if (pcmOutputFile_.is_open()) {
174 pcmOutputFile_.close();
175 }
176 }
177
CreateDec()178 int32_t ADecDemo::CreateDec()
179 {
180 if (audioType_ == TYPE_AAC) {
181 audioDec_ = OH_AudioDecoder_CreateByName((AVCodecCodecName::AUDIO_DECODER_AAC_NAME).data());
182 } else if (audioType_ == TYPE_FLAC) {
183 audioDec_ = OH_AudioDecoder_CreateByName((AVCodecCodecName::AUDIO_DECODER_FLAC_NAME).data());
184 } else if (audioType_ == TYPE_MP3) {
185 audioDec_ = OH_AudioDecoder_CreateByName((AVCodecCodecName::AUDIO_DECODER_MP3_NAME).data());
186 } else if (audioType_ == TYPE_VORBIS) {
187 audioDec_ = OH_AudioDecoder_CreateByName((AVCodecCodecName::AUDIO_DECODER_VORBIS_NAME).data());
188 } else {
189 return AVCS_ERR_INVALID_VAL;
190 }
191 DEMO_CHECK_AND_RETURN_RET_LOG(audioDec_ != nullptr, AVCS_ERR_UNKNOWN, "Fatal: CreateByName fail");
192
193 signal_ = new ADecSignal();
194 DEMO_CHECK_AND_RETURN_RET_LOG(signal_ != nullptr, AVCS_ERR_UNKNOWN, "Fatal: No memory");
195
196 cb_ = {&OnError, &OnOutputFormatChanged, &OnInputBufferAvailable, &OnOutputBufferAvailable};
197 int32_t ret = OH_AudioDecoder_SetCallback(audioDec_, cb_, signal_);
198 DEMO_CHECK_AND_RETURN_RET_LOG(ret == AVCS_ERR_OK, AVCS_ERR_UNKNOWN, "Fatal: SetCallback fail");
199
200 return AVCS_ERR_OK;
201 }
202
Configure(OH_AVFormat * format)203 int32_t ADecDemo::Configure(OH_AVFormat *format)
204 {
205 return OH_AudioDecoder_Configure(audioDec_, format);
206 }
207
Start()208 int32_t ADecDemo::Start()
209 {
210 isRunning_.store(true);
211
212 inputLoop_ = make_unique<thread>(&ADecDemo::InputFunc, this);
213 DEMO_CHECK_AND_RETURN_RET_LOG(inputLoop_ != nullptr, AVCS_ERR_UNKNOWN, "Fatal: No memory");
214
215 outputLoop_ = make_unique<thread>(&ADecDemo::OutputFunc, this);
216 DEMO_CHECK_AND_RETURN_RET_LOG(outputLoop_ != nullptr, AVCS_ERR_UNKNOWN, "Fatal: No memory");
217
218 return OH_AudioDecoder_Start(audioDec_);
219 }
220
Stop()221 int32_t ADecDemo::Stop()
222 {
223 isRunning_.store(false);
224 if (inputLoop_ != nullptr && inputLoop_->joinable()) {
225 {
226 unique_lock<mutex> lock(signal_->inMutex_);
227 signal_->inCond_.notify_all();
228 }
229 inputLoop_->join();
230 inputLoop_ = nullptr;
231 while (!signal_->inQueue_.empty()) {
232 signal_->inQueue_.pop();
233 }
234 while (!signal_->inBufferQueue_.empty()) {
235 signal_->inBufferQueue_.pop();
236 }
237 }
238
239 if (outputLoop_ != nullptr && outputLoop_->joinable()) {
240 {
241 unique_lock<mutex> lock(signal_->outMutex_);
242 signal_->outCond_.notify_all();
243 }
244 outputLoop_->join();
245 outputLoop_ = nullptr;
246 while (!signal_->outQueue_.empty()) {
247 signal_->outQueue_.pop();
248 }
249 while (!signal_->attrQueue_.empty()) {
250 signal_->attrQueue_.pop();
251 }
252 while (!signal_->outBufferQueue_.empty()) {
253 signal_->outBufferQueue_.pop();
254 }
255 }
256 std::cout << "start stop!\n";
257 return OH_AudioDecoder_Stop(audioDec_);
258 }
259
Flush()260 int32_t ADecDemo::Flush()
261 {
262 isRunning_.store(false);
263 if (inputLoop_ != nullptr && inputLoop_->joinable()) {
264 {
265 unique_lock<mutex> lock(signal_->inMutex_);
266 signal_->inCond_.notify_all();
267 }
268 inputLoop_->join();
269 inputLoop_ = nullptr;
270 while (!signal_->inQueue_.empty()) {
271 signal_->inQueue_.pop();
272 }
273 while (!signal_->inBufferQueue_.empty()) {
274 signal_->inBufferQueue_.pop();
275 }
276 std::cout << "clear input buffer!\n";
277 }
278
279 if (outputLoop_ != nullptr && outputLoop_->joinable()) {
280 {
281 unique_lock<mutex> lock(signal_->outMutex_);
282 signal_->outCond_.notify_all();
283 }
284 outputLoop_->join();
285 outputLoop_ = nullptr;
286 while (!signal_->outQueue_.empty()) {
287 signal_->outQueue_.pop();
288 }
289 while (!signal_->attrQueue_.empty()) {
290 signal_->attrQueue_.pop();
291 }
292 while (!signal_->outBufferQueue_.empty()) {
293 signal_->outBufferQueue_.pop();
294 }
295 std::cout << "clear output buffer!\n";
296 }
297 return OH_AudioDecoder_Flush(audioDec_);
298 }
299
Reset()300 int32_t ADecDemo::Reset()
301 {
302 return OH_AudioDecoder_Reset(audioDec_);
303 }
304
Release()305 int32_t ADecDemo::Release()
306 {
307 return OH_AudioDecoder_Destroy(audioDec_);
308 }
309
HandleInputEOS(const uint32_t index)310 void ADecDemo::HandleInputEOS(const uint32_t index)
311 {
312 OH_AVCodecBufferAttr info;
313 info.size = 0;
314 info.offset = 0;
315 info.pts = 0;
316 info.flags = AVCODEC_BUFFER_FLAGS_EOS;
317 OH_AudioDecoder_PushInputData(audioDec_, index, info);
318 signal_->inBufferQueue_.pop();
319 signal_->inQueue_.pop();
320 }
321
HandleNormalInput(const uint32_t & index,const int64_t pts,const size_t size)322 int32_t ADecDemo::HandleNormalInput(const uint32_t &index, const int64_t pts, const size_t size)
323 {
324 OH_AVCodecBufferAttr info;
325 info.size = size;
326 info.offset = 0;
327 info.pts = pts;
328
329 int32_t ret = AVCS_ERR_OK;
330 if (isFirstFrame_) {
331 info.flags = AVCODEC_BUFFER_FLAGS_CODEC_DATA;
332 ret = OH_AudioDecoder_PushInputData(audioDec_, index, info);
333 isFirstFrame_ = false;
334 } else {
335 info.flags = AVCODEC_BUFFER_FLAGS_NONE;
336 ret = OH_AudioDecoder_PushInputData(audioDec_, index, info);
337 }
338 signal_->inQueue_.pop();
339 signal_->inBufferQueue_.pop();
340 frameCount_++;
341 return ret;
342 }
343
InputFunc()344 void ADecDemo::InputFunc()
345 {
346 int64_t size;
347 int64_t pts;
348
349 while (true) {
350 if (!isRunning_.load()) {
351 break;
352 }
353 unique_lock<mutex> lock(signal_->inMutex_);
354 signal_->inCond_.wait(lock, [this]() { return (signal_->inQueue_.size() > 0 || !isRunning_.load()); });
355
356 if (!isRunning_.load()) {
357 break;
358 }
359
360 uint32_t index = signal_->inQueue_.front();
361 auto buffer = signal_->inBufferQueue_.front();
362 DEMO_CHECK_AND_BREAK_LOG(buffer != nullptr, "Fatal: GetInputBuffer fail");
363 inputFile_.read(reinterpret_cast<char *>(&size), sizeof(size));
364 if (inputFile_.eof() || inputFile_.gcount() == 0 || size == 0) {
365 HandleInputEOS(index);
366 std::cout << "end buffer\n";
367 break;
368 }
369 DEMO_CHECK_AND_BREAK_LOG(inputFile_.gcount() == sizeof(size), "Fatal: read size fail");
370 inputFile_.read(reinterpret_cast<char *>(&pts), sizeof(pts));
371 DEMO_CHECK_AND_BREAK_LOG(inputFile_.gcount() == sizeof(pts), "Fatal: read pts fail");
372 inputFile_.read((char *)OH_AVMemory_GetAddr(buffer), size);
373 DEMO_CHECK_AND_BREAK_LOG(inputFile_.gcount() == size, "Fatal: read buffer fail");
374
375 int32_t ret = HandleNormalInput(index, pts, size);
376 if (ret != AVCS_ERR_OK) {
377 cout << "Fatal error, exit" << endl;
378 break;
379 }
380 }
381 inputFile_.close();
382 }
383
OutputFunc()384 void ADecDemo::OutputFunc()
385 {
386 DEMO_CHECK_AND_RETURN_LOG(pcmOutputFile_.is_open(), "Fatal: output file failedis not open");
387 while (true) {
388 if (!isRunning_.load()) {
389 cout << "stop, exit" << endl;
390 break;
391 }
392
393 unique_lock<mutex> lock(signal_->outMutex_);
394 signal_->outCond_.wait(lock, [this]() { return (signal_->outQueue_.size() > 0 || !isRunning_.load()); });
395
396 if (!isRunning_.load()) {
397 cout << "wait to stop, exit" << endl;
398 break;
399 }
400
401 uint32_t index = signal_->outQueue_.front();
402 OH_AVCodecBufferAttr attr = signal_->attrQueue_.front();
403 OH_AVMemory *data = signal_->outBufferQueue_.front();
404 if (attr.flags != AVCODEC_BUFFER_FLAGS_EOS && data != nullptr) {
405 pcmOutputFile_.write(reinterpret_cast<char *>(OH_AVMemory_GetAddr(data)), attr.size);
406 }
407
408 if (attr.flags == AVCODEC_BUFFER_FLAGS_EOS) {
409 cout << "decode eos" << endl;
410 isRunning_.store(false);
411 signal_->startCond_.notify_all();
412 }
413 signal_->outBufferQueue_.pop();
414 signal_->attrQueue_.pop();
415 signal_->outQueue_.pop();
416 if (OH_AudioDecoder_FreeOutputData(audioDec_, index) != AV_ERR_OK) {
417 cout << "Fatal: FreeOutputData fail" << endl;
418 break;
419 }
420 }
421 pcmOutputFile_.close();
422 }
423