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_opus_encoder_demo.h"
17 #include <iostream>
18 #include <unistd.h>
19 #include "securec.h"
20 #include "avcodec_common.h"
21 #include "avcodec_errors.h"
22 #include "media_description.h"
23 #include "native_avformat.h"
24 #include "demo_log.h"
25 #include "avcodec_codec_name.h"
26 #include "ffmpeg_converter.h"
27 
28 using namespace OHOS;
29 using namespace OHOS::MediaAVCodec;
30 using namespace OHOS::MediaAVCodec::AudioOpusDemo;
31 using namespace std;
32 
33 namespace {
34 constexpr int32_t CHANNEL_COUNT = 1;
35 constexpr int32_t SAM_RATE_COUNT = 8000;
36 constexpr int32_t BIT_RATE_COUNT = 150000;
37 constexpr int32_t BIT_PER_CODE_COUNT = 16;
38 constexpr int32_t COMPLEXITY_COUNT = 10;
39 
40 constexpr string_view INPUT_FILE_PATH = "/data/test/media/test.pcm";
41 constexpr string_view OUTPUT_FILE_PATH = "/data/test/media/opus_encoder_test.opus";
42 } // namespace
43 
OnError(OH_AVCodec * codec,int32_t errorCode,void * userData)44 static void OnError(OH_AVCodec *codec, int32_t errorCode, void *userData)
45 {
46     (void)codec;
47     (void)errorCode;
48     (void)userData;
49     cout << "Error received, errorCode:" << errorCode << endl;
50 }
51 
OnOutputFormatChanged(OH_AVCodec * codec,OH_AVFormat * format,void * userData)52 static void OnOutputFormatChanged(OH_AVCodec *codec, OH_AVFormat *format, void *userData)
53 {
54     (void)codec;
55     (void)format;
56     (void)userData;
57     cout << "OnOutputFormatChanged received" << endl;
58 }
59 
OnInputBufferAvailable(OH_AVCodec * codec,uint32_t index,OH_AVMemory * data,void * userData)60 static void OnInputBufferAvailable(OH_AVCodec *codec, uint32_t index, OH_AVMemory *data, void *userData)
61 {
62     (void)codec;
63     AEncSignal *signal = static_cast<AEncSignal *>(userData);
64     unique_lock<mutex> lock(signal->inMutex_);
65     signal->inQueue_.push(index);
66     signal->inBufferQueue_.push(data);
67     signal->inCond_.notify_all();
68 }
69 
OnOutputBufferAvailable(OH_AVCodec * codec,uint32_t index,OH_AVMemory * data,OH_AVCodecBufferAttr * attr,void * userData)70 static void OnOutputBufferAvailable(OH_AVCodec *codec, uint32_t index, OH_AVMemory *data, OH_AVCodecBufferAttr *attr,
71                                     void *userData)
72 {
73     (void)codec;
74     AEncSignal *signal = static_cast<AEncSignal *>(userData);
75     unique_lock<mutex> lock(signal->outMutex_);
76     signal->outQueue_.push(index);
77     signal->outBufferQueue_.push(data);
78     if (attr) {
79         cout << "OnOutputBufferAvailable received, index:" << index << ", attr->size:" << attr->size
80              << ", attr->flags:" << attr->flags << ", pts " << attr->pts << endl;
81         signal->attrQueue_.push(*attr);
82     } else {
83         cout << "OnOutputBufferAvailable error, attr is nullptr!" << endl;
84     }
85     signal->outCond_.notify_all();
86 }
87 
RunCase()88 void AEncOpusDemo::RunCase()
89 {
90     CreateEnc();
91     OH_AVFormat *format = OH_AVFormat_Create();
92     OH_AVFormat_SetIntValue(format, MediaDescriptionKey::MD_KEY_CHANNEL_COUNT.data(), CHANNEL_COUNT);
93     OH_AVFormat_SetIntValue(format, MediaDescriptionKey::MD_KEY_SAMPLE_RATE.data(), SAM_RATE_COUNT);
94     OH_AVFormat_SetLongValue(format, MediaDescriptionKey::MD_KEY_BITRATE.data(), BIT_RATE_COUNT);
95     OH_AVFormat_SetIntValue(format, MediaDescriptionKey::MD_KEY_AUDIO_SAMPLE_FORMAT.data(),
96         AudioSampleFormat::SAMPLE_S16LE);
97     OH_AVFormat_SetIntValue(format, MediaDescriptionKey::MD_KEY_BITS_PER_CODED_SAMPLE.data(), BIT_PER_CODE_COUNT);
98     OH_AVFormat_SetIntValue(format, MediaDescriptionKey::MD_KEY_COMPLIANCE_LEVEL.data(), COMPLEXITY_COUNT);
99     Configure(format);
100     Start();
101     {
102         unique_lock<mutex> lock(signal_->startMutex_);
103         signal_->startCond_.wait(lock, [this]() { return (!(isRunning_.load())); });
104     }
105     Stop();
106     Release();
107 }
108 
AEncOpusDemo()109 AEncOpusDemo::AEncOpusDemo()
110     : isRunning_(false),
111       inputFile_(std::make_unique<std::ifstream>(INPUT_FILE_PATH, std::ios::binary)),
112       outputFile_(std::make_unique<std::ofstream>(OUTPUT_FILE_PATH, std::ios::binary)),
113       audioEnc_(nullptr),
114       signal_(nullptr),
115       frameCount_(0)
116 {
117 }
118 
~AEncOpusDemo()119 AEncOpusDemo::~AEncOpusDemo()
120 {
121     if (signal_) {
122         delete signal_;
123         signal_ = nullptr;
124     }
125 }
126 
CreateEnc()127 int32_t AEncOpusDemo::CreateEnc()
128 {
129     audioEnc_ = OH_AudioEncoder_CreateByName((AVCodecCodecName::AUDIO_ENCODER_OPUS_NAME).data());
130     signal_ = new AEncSignal();
131     cb_ = {&OnError, &OnOutputFormatChanged, &OnInputBufferAvailable, &OnOutputBufferAvailable};
132 
133     isFirstFrame_ = true;
134     frameCount_ = 0;
135 
136     int32_t ret = OH_AudioEncoder_SetCallback(audioEnc_, cb_, signal_);
137     DEMO_CHECK_AND_RETURN_RET_LOG(ret == AVCS_ERR_OK, AVCS_ERR_UNKNOWN, "Fatal: SetCallback fail");
138 
139     return AVCS_ERR_OK;
140 }
141 
Configure(OH_AVFormat * format)142 int32_t AEncOpusDemo::Configure(OH_AVFormat *format)
143 {
144     return OH_AudioEncoder_Configure(audioEnc_, format);
145 }
146 
Start()147 int32_t AEncOpusDemo::Start()
148 {
149     isRunning_.store(true);
150 
151     inputLoop_ = make_unique<thread>(&AEncOpusDemo::InputFunc, this);
152     DEMO_CHECK_AND_RETURN_RET_LOG(inputLoop_ != nullptr, AVCS_ERR_UNKNOWN, "Fatal: No memory");
153 
154     outputLoop_ = make_unique<thread>(&AEncOpusDemo::OutputFunc, this);
155     DEMO_CHECK_AND_RETURN_RET_LOG(outputLoop_ != nullptr, AVCS_ERR_UNKNOWN, "Fatal: No memory");
156 
157     return OH_AudioEncoder_Start(audioEnc_);
158 }
159 
Stop()160 int32_t AEncOpusDemo::Stop()
161 {
162     isRunning_.store(false);
163 
164     if (inputLoop_ != nullptr && inputLoop_->joinable()) {
165         {
166             unique_lock<mutex> lock(signal_->inMutex_);
167             signal_->inCond_.notify_all();
168         }
169         inputLoop_->join();
170         inputLoop_ = nullptr;
171         while (!signal_->inQueue_.empty()) {
172             signal_->inQueue_.pop();
173         }
174         while (!signal_->inBufferQueue_.empty()) {
175             signal_->inBufferQueue_.pop();
176         }
177     }
178 
179     if (outputLoop_ != nullptr && outputLoop_->joinable()) {
180         {
181             unique_lock<mutex> lock(signal_->outMutex_);
182             signal_->outCond_.notify_all();
183         }
184         outputLoop_->join();
185         outputLoop_ = nullptr;
186         while (!signal_->outQueue_.empty()) {
187             signal_->outQueue_.pop();
188         }
189         while (!signal_->attrQueue_.empty()) {
190             signal_->attrQueue_.pop();
191         }
192         while (!signal_->outBufferQueue_.empty()) {
193             signal_->outBufferQueue_.pop();
194         }
195     }
196 
197     return OH_AudioEncoder_Stop(audioEnc_);
198 }
199 
Flush()200 int32_t AEncOpusDemo::Flush()
201 {
202     return 0;
203 }
204 
Reset()205 int32_t AEncOpusDemo::Reset()
206 {
207     return 0;
208 }
209 
Release()210 int32_t AEncOpusDemo::Release()
211 {
212     return 0;
213 }
214 
HandleEOS(const uint32_t & index)215 void AEncOpusDemo::HandleEOS(const uint32_t &index)
216 {
217     OH_AVCodecBufferAttr info;
218     info.size = 0;
219     info.offset = 0;
220     info.pts = 0;
221     info.flags = AVCODEC_BUFFER_FLAGS_EOS;
222     OH_AudioEncoder_PushInputData(audioEnc_, index, info);
223     std::cout << "end buffer\n";
224     signal_->inQueue_.pop();
225     signal_->inBufferQueue_.pop();
226 }
227 
GetFrameBytes()228 static int32_t GetFrameBytes()
229 {
230     int32_t frameBytes = CHANNEL_COUNT * sizeof(short) * SAM_RATE_COUNT * 0.02;
231     return frameBytes;
232 }
233 
InputFunc()234 void AEncOpusDemo::InputFunc()
235 {
236     auto frameBytes = GetFrameBytes();
237     DEMO_CHECK_AND_RETURN_LOG(inputFile_ != nullptr && inputFile_->is_open(), "Fatal: open file fail");
238     while (isRunning_.load()) {
239         cout << "encode 2" << endl;
240         unique_lock<mutex> lock(signal_->inMutex_);
241         signal_->inCond_.wait(lock, [this]() { return (signal_->inQueue_.size() > 0 || !isRunning_.load()); });
242         if (!isRunning_.load()) {
243             break;
244         }
245         uint32_t index = signal_->inQueue_.front();
246         auto buffer = signal_->inBufferQueue_.front();
247         DEMO_CHECK_AND_BREAK_LOG(buffer != nullptr, "Fatal: GetInputBuffer fail");
248         if (!inputFile_->eof()) {
249             inputFile_->read(reinterpret_cast<char *>(OH_AVMemory_GetAddr(buffer)), frameBytes);
250         } else {
251             cout << "eofeofeofeofeof" << endl;
252             HandleEOS(index);
253             break;
254         }
255         DEMO_CHECK_AND_BREAK_LOG(buffer != nullptr, "Fatal: GetInputBuffer fail");
256         OH_AVCodecBufferAttr info;
257         info.size = frameBytes;
258         info.offset = 0;
259 
260         int32_t ret = AVCS_ERR_OK;
261         if (isFirstFrame_) {
262             info.flags = AVCODEC_BUFFER_FLAGS_CODEC_DATA;
263             ret = OH_AudioEncoder_PushInputData(audioEnc_, index, info);
264             isFirstFrame_ = false;
265         } else {
266             info.flags = AVCODEC_BUFFER_FLAGS_NONE;
267             ret = OH_AudioEncoder_PushInputData(audioEnc_, index, info);
268         }
269 
270         signal_->inQueue_.pop();
271         signal_->inBufferQueue_.pop();
272         frameCount_++;
273         if (ret != AVCS_ERR_OK) {
274             cout << "Fatal error, exit" << endl;
275             isRunning_ = false;
276             break;
277         }
278     }
279     inputFile_->close();
280 }
281 
OutputFunc()282 void AEncOpusDemo::OutputFunc()
283 {
284     DEMO_CHECK_AND_RETURN_LOG(outputFile_ != nullptr && outputFile_->is_open(), "Fatal: open output file fail");
285     int64_t codesize;
286     while (isRunning_.load()) {
287         unique_lock<mutex> lock(signal_->outMutex_);
288         signal_->outCond_.wait(lock, [this]() { return (signal_->outQueue_.size() > 0 || !isRunning_.load()); });
289 
290         if (!isRunning_.load()) {
291             cout << "wait to stop, exit" << endl;
292             break;
293         }
294 
295         uint32_t index = signal_->outQueue_.front();
296         cout << "encode 1" << endl;
297         OH_AVCodecBufferAttr attr = signal_->attrQueue_.front();
298         OH_AVMemory *data = signal_->outBufferQueue_.front();
299         if (data != nullptr) {
300             codesize = attr.size;
301             outputFile_->write(reinterpret_cast<char *>(&codesize), sizeof(int64_t));
302             outputFile_->write(reinterpret_cast<char *>(&codesize), sizeof(int64_t));
303             outputFile_->write(reinterpret_cast<char *>(OH_AVMemory_GetAddr(data)), attr.size);
304         }
305         if (attr.flags == AVCODEC_BUFFER_FLAGS_EOS || attr.size == 0) {
306             cout << "encode eos" << endl;
307             isRunning_.store(false);
308             signal_->startCond_.notify_all();
309         }
310         signal_->outBufferQueue_.pop();
311         signal_->attrQueue_.pop();
312         signal_->outQueue_.pop();
313         if (OH_AudioEncoder_FreeOutputData(audioEnc_, index) != AV_ERR_OK) {
314             cout << "Fatal: FreeOutputData fail" << endl;
315             break;
316         }
317         if (attr.flags == AVCODEC_BUFFER_FLAGS_EOS) {
318             cout << "decode eos" << endl;
319             isRunning_.store(false);
320             signal_->startCond_.notify_all();
321         }
322     }
323     outputFile_->close();
324 }
325