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_amrnb_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::AudioAvbufferAmrNbDemo;
35 using namespace std;
36 namespace {
37 constexpr uint32_t CHANNEL_COUNT = 1;
38 constexpr uint32_t SAMPLE_RATE = 8000;
39 constexpr int64_t BIT_RATE = 10200;
40 constexpr int32_t SAMPLE_FORMAT = AudioSampleFormat::SAMPLE_S16LE;
41 constexpr int32_t INPUT_FRAME_BYTES = 320;  // 20ms
42 
43 constexpr string_view INPUT_FILE_PATH = "/data/test/media/amrnb_8kHz_10s.pcm";
44 constexpr string_view OUTPUT_FILE_PATH = "/data/test/media/amrnb_8kHz_10s_afterEncode.raw";
45 }  // namespace
46 
OnError(OH_AVCodec * codec,int32_t errorCode,void * userData)47 static void OnError(OH_AVCodec* codec, int32_t errorCode, void* userData)
48 {
49     (void)codec;
50     (void)errorCode;
51     (void)userData;
52     cout << "Error received, errorCode:" << errorCode << endl;
53 }
54 
OnOutputFormatChanged(OH_AVCodec * codec,OH_AVFormat * format,void * userData)55 static void OnOutputFormatChanged(OH_AVCodec* codec, OH_AVFormat* format, void* userData)
56 {
57     (void)codec;
58     (void)format;
59     (void)userData;
60     cout << "OnOutputFormatChanged received" << endl;
61 }
62 
OnInputBufferAvailable(OH_AVCodec * codec,uint32_t index,OH_AVBuffer * buffer,void * userData)63 static void OnInputBufferAvailable(OH_AVCodec *codec, uint32_t index, OH_AVBuffer *buffer, void *userData)
64 {
65     (void)codec;
66     AEncSignal* signal = static_cast<AEncSignal*>(userData);
67     unique_lock<mutex> lock(signal->inMutex_);
68     signal->inQueue_.push(index);
69     signal->inBufferQueue_.push(buffer);
70     signal->inCond_.notify_all();
71 }
72 
OnOutputBufferAvailable(OH_AVCodec * codec,uint32_t index,OH_AVBuffer * buffer,void * userData)73 static void OnOutputBufferAvailable(OH_AVCodec *codec, uint32_t index, OH_AVBuffer *buffer, void *userData)
74 {
75     (void)codec;
76     AEncSignal* signal = static_cast<AEncSignal*>(userData);
77     unique_lock<mutex> lock(signal->outMutex_);
78     signal->outQueue_.push(index);
79     signal->outBufferQueue_.push(buffer);
80     if (buffer) {
81         cout << "OnOutputBufferAvailable received, index:" << index << ", size:" << buffer->buffer_->memory_->GetSize()
82              << ", flags:" << buffer->buffer_->flag_ << ", pts: " << buffer->buffer_->pts_ << endl;
83     } else {
84         cout << "OnOutputBufferAvailable error, attr is nullptr!" << endl;
85     }
86     signal->outCond_.notify_all();
87 }
88 
RunCase()89 void AEncAvbufferAmrNbDemo::RunCase()
90 {
91     std::cout << "amrnb RunCase enter" << std::endl;
92     DEMO_CHECK_AND_RETURN_LOG(CreateEnc() == AVCS_ERR_OK, "Fatal: CreateEnc fail");
93 
94     OH_AVFormat* format = OH_AVFormat_Create();
95     OH_AVFormat_SetIntValue(format, MediaDescriptionKey::MD_KEY_CHANNEL_COUNT.data(), CHANNEL_COUNT);
96     OH_AVFormat_SetIntValue(format, MediaDescriptionKey::MD_KEY_SAMPLE_RATE.data(), SAMPLE_RATE);
97     OH_AVFormat_SetLongValue(format, MediaDescriptionKey::MD_KEY_BITRATE.data(), BIT_RATE);
98     OH_AVFormat_SetIntValue(format, MediaDescriptionKey::MD_KEY_AUDIO_SAMPLE_FORMAT.data(), SAMPLE_FORMAT);
99 
100     DEMO_CHECK_AND_RETURN_LOG(Configure(format) == AVCS_ERR_OK, "Fatal: Configure fail");
101 
102     auto fmt = OH_AudioCodec_GetOutputDescription(audioEnc_);
103     int channels;
104     int sampleRate;
105     int64_t bitRate;
106     int sampleFormat;
107     int64_t channelLayout;
108     int frameSize;
109     OH_AVFormat_GetIntValue(fmt, MediaDescriptionKey::MD_KEY_CHANNEL_COUNT.data(), &channels);
110     OH_AVFormat_GetIntValue(fmt, MediaDescriptionKey::MD_KEY_SAMPLE_RATE.data(), &sampleRate);
111     OH_AVFormat_GetLongValue(fmt, MediaDescriptionKey::MD_KEY_BITRATE.data(), &bitRate);
112     OH_AVFormat_GetIntValue(fmt, MediaDescriptionKey::MD_KEY_AUDIO_SAMPLE_FORMAT.data(), &sampleFormat);
113     OH_AVFormat_GetLongValue(fmt, MediaDescriptionKey::MD_KEY_CHANNEL_LAYOUT.data(), &channelLayout);
114     OH_AVFormat_GetIntValue(fmt, MediaDescriptionKey::MD_KEY_AUDIO_SAMPLES_PER_FRAME.data(), &frameSize);
115     std::cout << "GetOutputDescription "
116               << "channels: " << channels
117               << ", sampleRate: " << sampleRate
118               << ", bitRate: " << bitRate
119               << ", sampleFormat: " << sampleFormat
120               << ", channelLayout: " << channelLayout
121               << ", frameSize: " << frameSize << std::endl;
122 
123     DEMO_CHECK_AND_RETURN_LOG(Start() == AVCS_ERR_OK, "Fatal: Start fail");
124     auto start = chrono::steady_clock::now();
125 
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 AEncAvbufferAmrNbDemo::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 
AEncAvbufferAmrNbDemo()153 AEncAvbufferAmrNbDemo::AEncAvbufferAmrNbDemo()
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 
~AEncAvbufferAmrNbDemo()161 AEncAvbufferAmrNbDemo::~AEncAvbufferAmrNbDemo()
162 {
163     if (signal_) {
164         delete signal_;
165         signal_ = nullptr;
166     }
167 }
168 
CreateEnc()169 int32_t AEncAvbufferAmrNbDemo::CreateEnc()
170 {
171     audioEnc_ = OH_AudioCodec_CreateByName((AVCodecCodecName::AUDIO_ENCODER_AMRNB_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 AEncAvbufferAmrNbDemo::Configure(OH_AVFormat *format)
185 {
186     return OH_AudioCodec_Configure(audioEnc_, format);
187 }
188 
Start()189 int32_t AEncAvbufferAmrNbDemo::Start()
190 {
191     isRunning_.store(true);
192 
193     inputLoop_ = make_unique<thread>(&AEncAvbufferAmrNbDemo::InputFunc, this);
194     DEMO_CHECK_AND_RETURN_RET_LOG(inputLoop_ != nullptr, AVCS_ERR_UNKNOWN, "Fatal: No memory");
195 
196     outputLoop_ = make_unique<thread>(&AEncAvbufferAmrNbDemo::OutputFunc, this);
197     DEMO_CHECK_AND_RETURN_RET_LOG(outputLoop_ != nullptr, AVCS_ERR_UNKNOWN, "Fatal: No memory");
198 
199     return OH_AudioCodec_Start(audioEnc_);
200 }
201 
Stop()202 int32_t AEncAvbufferAmrNbDemo::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 AEncAvbufferAmrNbDemo::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 AEncAvbufferAmrNbDemo::Reset()
276 {
277     return OH_AudioCodec_Reset(audioEnc_);
278 }
279 
Release()280 int32_t AEncAvbufferAmrNbDemo::Release()
281 {
282     return OH_AudioCodec_Destroy(audioEnc_);
283 }
284 
HandleEOS(const uint32_t & index)285 void AEncAvbufferAmrNbDemo::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 AEncAvbufferAmrNbDemo::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         signal_->inQueue_.pop();
335         signal_->inBufferQueue_.pop();
336         frameCount_++;
337         if (ret != AVCS_ERR_OK) {
338             cout << "Fatal error, exit" << endl;
339             break;
340         }
341     }
342     inputFile_->close();
343 }
344 
OutputFunc()345 void AEncAvbufferAmrNbDemo::OutputFunc()
346 {
347     DEMO_CHECK_AND_RETURN_LOG(outputFile_ != nullptr && outputFile_->is_open(), "Fatal: open output file fail");
348     while (isRunning_.load()) {
349         if (!isRunning_.load()) {
350             cout << "stop, exit" << endl;
351             break;
352         }
353 
354         unique_lock<mutex> lock(signal_->outMutex_);
355         signal_->outCond_.wait(lock, [this]() { return (signal_->outQueue_.size() > 0 || !isRunning_.load()); });
356 
357         if (!isRunning_.load()) {
358             cout << "wait to stop, exit" << endl;
359             break;
360         }
361 
362         uint32_t index = signal_->outQueue_.front();
363         OH_AVBuffer* avBuffer = signal_->outBufferQueue_.front();
364         if (avBuffer == nullptr) {
365             cout << "OutputFunc OH_AVBuffer is nullptr" << endl;
366             continue;
367         }
368         if (avBuffer != nullptr) {
369             cout << "OutputFunc write index:" << index << ", size:" << avBuffer->buffer_->memory_->GetSize() << endl;
370             outputFile_->write(reinterpret_cast<char*>(OH_AVBuffer_GetAddr(avBuffer)),
371                 avBuffer->buffer_->memory_->GetSize());
372         }
373         if (avBuffer != nullptr && (avBuffer->buffer_->flag_ == AVCODEC_BUFFER_FLAGS_EOS ||
374             avBuffer->buffer_->memory_->GetSize() == 0)) {
375             cout << "encode eos" << endl;
376             isRunning_.store(false);
377             signal_->startCond_.notify_all();
378         }
379 
380         signal_->outBufferQueue_.pop();
381         signal_->outQueue_.pop();
382         if (OH_AudioCodec_FreeOutputBuffer(audioEnc_, index) != AV_ERR_OK) {
383             cout << "Fatal: FreeOutputData fail" << endl;
384             break;
385         }
386         if (avBuffer->buffer_->flag_ == AVCODEC_BUFFER_FLAGS_EOS) {
387             cout << "amrnb encode eos" << endl;
388             isRunning_.store(false);
389             signal_->startCond_.notify_all();
390         }
391     }
392     outputFile_->close();
393 }