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