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_flac_encoder_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 "native_avbuffer.h"
28 #include "native_avmemory.h"
29 #include "securec.h"
30 #include "ffmpeg_converter.h"
31 
32 using namespace OHOS;
33 using namespace OHOS::MediaAVCodec;
34 using namespace OHOS::MediaAVCodec::AudioFlacEncDemo;
35 using namespace std;
36 namespace {
37 constexpr uint32_t CHANNEL_COUNT = 2;
38 constexpr uint32_t SAMPLE_RATE = 44100;
39 constexpr uint32_t BITS_RATE = 261000;
40 constexpr uint32_t BITS_PER_CODED_SAMPLE = OH_BitsPerSample::SAMPLE_S16LE;
41 constexpr uint64_t CHANNEL_LAYOUT = AudioChannelLayout::STEREO;
42 constexpr int32_t SAMPLE_FORMAT = AudioSampleFormat::SAMPLE_S16LE;
43 constexpr int32_t COMPLIANCE_LEVEL = 0;
44 constexpr int32_t DEFAULT_FRAME_BYTES = 1152;
45 static std::map<uint32_t, uint32_t> FRAME_BYTES_MAP = {{8000, 576}, {16000, 1152}, {22050, 2304}, {24000, 2304},
46                                                        {32000, 2304}, {44100, 4608}, {48000, 4608}, {88200, 8192},
47                                                        {96000, 8192}};
48 
49 constexpr string_view INPUT_FILE_PATH = "/data/test/media/flac_2c_44100hz_261k.pcm";
50 constexpr string_view OUTPUT_FILE_PATH = "/data/test/media/flac_encoder_test.flac";
51 } // namespace
52 
OnError(OH_AVCodec * codec,int32_t errorCode,void * userData)53 static void OnError(OH_AVCodec *codec, int32_t errorCode, void *userData)
54 {
55     (void)codec;
56     (void)errorCode;
57     (void)userData;
58     cout << "Error received, errorCode:" << errorCode << endl;
59 }
60 
OnOutputFormatChanged(OH_AVCodec * codec,OH_AVFormat * format,void * userData)61 static void OnOutputFormatChanged(OH_AVCodec *codec, OH_AVFormat *format, void *userData)
62 {
63     (void)codec;
64     (void)format;
65     (void)userData;
66     cout << "OnOutputFormatChanged received" << endl;
67 }
68 
OnInputBufferAvailable(OH_AVCodec * codec,uint32_t index,OH_AVBuffer * data,void * userData)69 static void OnInputBufferAvailable(OH_AVCodec *codec, uint32_t index, OH_AVBuffer *data, void *userData)
70 {
71     (void)codec;
72     AEncBufferSignal *signal = static_cast<AEncBufferSignal *>(userData);
73     unique_lock<mutex> lock(signal->inMutex_);
74     signal->inQueue_.push(index);
75     signal->inBufferQueue_.push(data);
76     signal->inCond_.notify_all();
77 }
78 
OnOutputBufferAvailable(OH_AVCodec * codec,uint32_t index,OH_AVBuffer * data,void * userData)79 static void OnOutputBufferAvailable(OH_AVCodec *codec, uint32_t index, OH_AVBuffer *data, void *userData)
80 {
81     (void)codec;
82     AEncBufferSignal *signal = static_cast<AEncBufferSignal *>(userData);
83     unique_lock<mutex> lock(signal->outMutex_);
84     signal->outQueue_.push(index);
85     signal->outBufferQueue_.push(data);
86     signal->outCond_.notify_all();
87 }
88 
InitFile()89 bool AudioBufferFlacEncDemo::InitFile()
90 {
91     inputFile_.open(INPUT_FILE_PATH, std::ios::binary);
92     outputFile_.open(OUTPUT_FILE_PATH.data(), std::ios::out | std::ios::binary);
93     DEMO_CHECK_AND_RETURN_RET_LOG(inputFile_.is_open(), false, "Fatal: open input file failed");
94     DEMO_CHECK_AND_RETURN_RET_LOG(outputFile_.is_open(), false, "Fatal: open output file failed");
95     return true;
96 }
97 
RunCase()98 void AudioBufferFlacEncDemo::RunCase()
99 {
100     cout << "RunCase Enter" << endl;
101     DEMO_CHECK_AND_RETURN_LOG(InitFile(), "Fatal: InitFile file failed");
102 
103     DEMO_CHECK_AND_RETURN_LOG(CreateEnc() == AVCS_ERR_OK, "Fatal: CreateEnc fail");
104 
105     OH_AVFormat *format = OH_AVFormat_Create();
106     OH_AVFormat_SetIntValue(format, MediaDescriptionKey::MD_KEY_CHANNEL_COUNT.data(), CHANNEL_COUNT);
107     OH_AVFormat_SetIntValue(format, MediaDescriptionKey::MD_KEY_SAMPLE_RATE.data(), SAMPLE_RATE);
108     OH_AVFormat_SetLongValue(format, MediaDescriptionKey::MD_KEY_BITRATE.data(), BITS_RATE);
109     OH_AVFormat_SetIntValue(format, MediaDescriptionKey::MD_KEY_BITS_PER_CODED_SAMPLE.data(), BITS_PER_CODED_SAMPLE);
110     OH_AVFormat_SetIntValue(format, MediaDescriptionKey::MD_KEY_AUDIO_SAMPLE_FORMAT.data(), SAMPLE_FORMAT);
111     OH_AVFormat_SetLongValue(format, MediaDescriptionKey::MD_KEY_CHANNEL_LAYOUT.data(), CHANNEL_LAYOUT);
112     OH_AVFormat_SetIntValue(format, MediaDescriptionKey::MD_KEY_COMPLIANCE_LEVEL.data(), COMPLIANCE_LEVEL);
113 
114     DEMO_CHECK_AND_RETURN_LOG(Configure(format) == AVCS_ERR_OK, "Fatal: Configure fail");
115 
116     DEMO_CHECK_AND_RETURN_LOG(Start() == AVCS_ERR_OK, "Fatal: Start fail");
117 
118     {
119         unique_lock<mutex> lock(signal_->startMutex_);
120         signal_->startCond_.wait(lock, [this]() { return (!(isRunning_.load())); });
121     }
122 
123     DEMO_CHECK_AND_RETURN_LOG(Stop() == AVCS_ERR_OK, "Fatal: Stop fail");
124     DEMO_CHECK_AND_RETURN_LOG(Release() == AVCS_ERR_OK, "Fatal: Release fail");
125 }
126 
AudioBufferFlacEncDemo()127 AudioBufferFlacEncDemo::AudioBufferFlacEncDemo() : audioEnc_(nullptr), signal_(nullptr) {}
128 
~AudioBufferFlacEncDemo()129 AudioBufferFlacEncDemo::~AudioBufferFlacEncDemo()
130 {
131     if (signal_) {
132         delete signal_;
133         signal_ = nullptr;
134     }
135     if (inputFile_.is_open()) {
136         inputFile_.close();
137     }
138     if (outputFile_.is_open()) {
139         outputFile_.close();
140     }
141 }
142 
CreateEnc()143 int32_t AudioBufferFlacEncDemo::CreateEnc()
144 {
145     audioEnc_ = OH_AudioCodec_CreateByName((AVCodecCodecName::AUDIO_ENCODER_FLAC_NAME).data());
146     DEMO_CHECK_AND_RETURN_RET_LOG(audioEnc_ != nullptr, AVCS_ERR_UNKNOWN, "Fatal: CreateByName fail");
147 
148     signal_ = new AEncBufferSignal();
149     DEMO_CHECK_AND_RETURN_RET_LOG(signal_ != nullptr, AVCS_ERR_UNKNOWN, "Fatal: No memory");
150 
151     cb_ = {&OnError, &OnOutputFormatChanged, &OnInputBufferAvailable, &OnOutputBufferAvailable};
152     int32_t ret = OH_AudioCodec_RegisterCallback(audioEnc_, cb_, signal_);
153     DEMO_CHECK_AND_RETURN_RET_LOG(ret == AVCS_ERR_OK, AVCS_ERR_UNKNOWN, "Fatal: SetCallback fail");
154 
155     return AVCS_ERR_OK;
156 }
157 
Configure(OH_AVFormat * format)158 int32_t AudioBufferFlacEncDemo::Configure(OH_AVFormat *format)
159 {
160     return OH_AudioCodec_Configure(audioEnc_, format);
161 }
162 
Start()163 int32_t AudioBufferFlacEncDemo::Start()
164 {
165     cout << "AudioBufferFlacEncDemo::Start Enter" << endl;
166     isRunning_.store(true);
167 
168     cout << "InputFunc" << endl;
169     inputLoop_ = make_unique<thread>(&AudioBufferFlacEncDemo::InputFunc, this);
170     DEMO_CHECK_AND_RETURN_RET_LOG(inputLoop_ != nullptr, AVCS_ERR_UNKNOWN, "Fatal: No memory");
171 
172     cout << "OutputFunc" << endl;
173     outputLoop_ = make_unique<thread>(&AudioBufferFlacEncDemo::OutputFunc, this);
174     DEMO_CHECK_AND_RETURN_RET_LOG(outputLoop_ != nullptr, AVCS_ERR_UNKNOWN, "Fatal: No memory");
175 
176     cout << "OH_AudioCodec_Start" << endl;
177     return OH_AudioCodec_Start(audioEnc_);
178 }
179 
Stop()180 int32_t AudioBufferFlacEncDemo::Stop()
181 {
182     isRunning_.store(false);
183 
184     if (inputLoop_ != nullptr && inputLoop_->joinable()) {
185         {
186             unique_lock<mutex> lock(signal_->inMutex_);
187             signal_->inCond_.notify_all();
188         }
189         inputLoop_->join();
190         inputLoop_ = nullptr;
191         while (!signal_->inQueue_.empty()) {
192             signal_->inQueue_.pop();
193         }
194         while (!signal_->inBufferQueue_.empty()) {
195             signal_->inBufferQueue_.pop();
196         }
197     }
198 
199     if (outputLoop_ != nullptr && outputLoop_->joinable()) {
200         {
201             unique_lock<mutex> lock(signal_->outMutex_);
202             signal_->outCond_.notify_all();
203         }
204         outputLoop_->join();
205         outputLoop_ = nullptr;
206         while (!signal_->outQueue_.empty()) {
207             signal_->outQueue_.pop();
208         }
209         while (!signal_->outBufferQueue_.empty()) {
210             signal_->outBufferQueue_.pop();
211         }
212     }
213 
214     return OH_AudioCodec_Stop(audioEnc_);
215 }
216 
Flush()217 int32_t AudioBufferFlacEncDemo::Flush()
218 {
219     isRunning_.store(false);
220     if (inputLoop_ != nullptr && inputLoop_->joinable()) {
221         {
222             unique_lock<mutex> lock(signal_->inMutex_);
223             signal_->inCond_.notify_all();
224         }
225         inputLoop_->join();
226         inputLoop_ = nullptr;
227         while (!signal_->inQueue_.empty()) {
228             signal_->inQueue_.pop();
229         }
230         while (!signal_->inBufferQueue_.empty()) {
231             signal_->inBufferQueue_.pop();
232         }
233         std::cout << "clear input buffer!\n";
234     }
235 
236     if (outputLoop_ != nullptr && outputLoop_->joinable()) {
237         {
238             unique_lock<mutex> lock(signal_->outMutex_);
239             signal_->outCond_.notify_all();
240         }
241         outputLoop_->join();
242         outputLoop_ = nullptr;
243         while (!signal_->outQueue_.empty()) {
244             signal_->outQueue_.pop();
245         }
246         while (!signal_->outBufferQueue_.empty()) {
247             signal_->outBufferQueue_.pop();
248         }
249         std::cout << "clear output buffer!\n";
250     }
251     return OH_AudioCodec_Flush(audioEnc_);
252 }
253 
Reset()254 int32_t AudioBufferFlacEncDemo::Reset()
255 {
256     return OH_AudioCodec_Reset(audioEnc_);
257 }
258 
Release()259 int32_t AudioBufferFlacEncDemo::Release()
260 {
261     return OH_AudioCodec_Destroy(audioEnc_);
262 }
263 
HandleEOS(const uint32_t & index)264 void AudioBufferFlacEncDemo::HandleEOS(const uint32_t &index)
265 {
266     OH_AudioCodec_PushInputBuffer(audioEnc_, index);
267     signal_->inQueue_.pop();
268     signal_->inBufferQueue_.pop();
269 }
270 
GetFrameBytes()271 static int32_t GetFrameBytes()
272 {
273     auto bitsPerSamples = (SAMPLE_FORMAT == AudioSampleFormat::SAMPLE_S16LE) ? 2 : 4;
274     auto iter = FRAME_BYTES_MAP.find(SAMPLE_RATE);
275     uint32_t frameSize = DEFAULT_FRAME_BYTES;
276     if (iter != FRAME_BYTES_MAP.end()) {
277         frameSize = iter->second;
278     }
279     int32_t frameBytes = CHANNEL_COUNT * bitsPerSamples * frameSize;
280     std::cout << "frameBytes : " << frameBytes << std::endl;
281     return frameBytes;
282 }
283 
InputFunc()284 void AudioBufferFlacEncDemo::InputFunc()
285 {
286     auto frameBytes = GetFrameBytes();
287     DEMO_CHECK_AND_RETURN_LOG(inputFile_.is_open(), "Fatal: open file fail");
288     while (isRunning_.load()) {
289         unique_lock<mutex> lock(signal_->inMutex_);
290         signal_->inCond_.wait(lock, [this]() { return (signal_->inQueue_.size() > 0 || !isRunning_.load()); });
291         if (!isRunning_.load()) {
292             break;
293         }
294         uint32_t index = signal_->inQueue_.front();
295         auto buffer = signal_->inBufferQueue_.front();
296         DEMO_CHECK_AND_BREAK_LOG(buffer != nullptr, "Fatal: GetInputBuffer fail");
297         OH_AVCodecBufferAttr attr = {0, 0, 0, 0};
298         if (!inputFile_.eof()) {
299             inputFile_.read(reinterpret_cast<char *>(OH_AVBuffer_GetAddr(buffer)), frameBytes);
300             attr.size = frameBytes;
301             OH_AVBuffer_SetBufferAttr(buffer, &attr);
302         } else {
303             attr.size = 0;
304             attr.flags = AVCODEC_BUFFER_FLAGS_EOS;
305             OH_AVBuffer_SetBufferAttr(buffer, &attr);
306             std::cout << "EOS "  << std::endl;
307             HandleEOS(index);
308             break;
309         }
310 
311         int32_t ret = AVCS_ERR_OK;
312         if (isFirstFrame_) {
313             buffer->buffer_->flag_ = AVCODEC_BUFFER_FLAGS_CODEC_DATA;
314             ret = OH_AudioCodec_PushInputBuffer(audioEnc_, index);
315             isFirstFrame_ = false;
316         } else {
317             buffer->buffer_->flag_ = AVCODEC_BUFFER_FLAGS_NONE;
318             ret = OH_AudioCodec_PushInputBuffer(audioEnc_, index);
319         }
320 
321         signal_->inQueue_.pop();
322         signal_->inBufferQueue_.pop();
323         if (ret != AVCS_ERR_OK) {
324             cout << "Fatal error, exit" << endl;
325             isRunning_ = false;
326             break;
327         }
328     }
329     inputFile_.close();
330 }
331 
OutputFunc()332 void AudioBufferFlacEncDemo::OutputFunc()
333 {
334     DEMO_CHECK_AND_RETURN_LOG(outputFile_.is_open(), "Fatal: open output file fail");
335     while (isRunning_.load()) {
336         unique_lock<mutex> lock(signal_->outMutex_);
337         signal_->outCond_.wait(lock, [this]() { return (signal_->outQueue_.size() > 0 || !isRunning_.load()); });
338 
339         if (!isRunning_.load()) {
340             cout << "wait to stop, exit" << endl;
341             break;
342         }
343 
344         uint32_t index = signal_->outQueue_.front();
345 
346         OH_AVBuffer *data = signal_->outBufferQueue_.front();
347         if (data != nullptr) {
348             outputFile_.write(reinterpret_cast<char *>(OH_AVBuffer_GetAddr(data)), data->buffer_->memory_->GetSize());
349         } else {
350             cout << "OutputFunc OH_AVBuffer is nullptr" << endl;
351             continue;
352         }
353 
354         if (data != nullptr &&
355             (data->buffer_->flag_ == AVCODEC_BUFFER_FLAGS_EOS || data->buffer_->memory_->GetSize() == 0)) {
356             cout << "encode eos" << endl;
357             isRunning_.store(false);
358             signal_->startCond_.notify_all();
359         }
360 
361         signal_->outBufferQueue_.pop();
362         signal_->outQueue_.pop();
363         if (OH_AudioCodec_FreeOutputBuffer(audioEnc_, index) != AV_ERR_OK) {
364             cout << "Fatal: FreeOutputData fail" << endl;
365             break;
366         }
367 
368         if (data->buffer_->flag_ == AVCODEC_BUFFER_FLAGS_EOS) {
369             cout << "encode eos" << endl;
370             isRunning_.store(false);
371             signal_->startCond_.notify_all();
372         }
373     }
374     outputFile_.close();
375 }
376