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_g711mu_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::AudioAvbufferG711muDemo;
35 using namespace std;
36 namespace {
37 constexpr uint32_t CHANNEL_COUNT = 1;
38 constexpr uint32_t SAMPLE_RATE = 8000;
39 constexpr uint32_t FRAME_DURATION_US = 33000;
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/g711mu_8kHz_10s.pcm";
44 constexpr string_view OUTPUT_FILE_PATH = "/data/test/media/g711mu_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 AEncAvbufferG711muDemo::RunCase()
90 {
91 std::cout << "G711 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_SetIntValue(format, MediaDescriptionKey::MD_KEY_AUDIO_SAMPLE_FORMAT.data(), SAMPLE_FORMAT);
98
99 DEMO_CHECK_AND_RETURN_LOG(Configure(format) == AVCS_ERR_OK, "Fatal: Configure fail");
100
101 auto fmt = OH_AudioCodec_GetOutputDescription(audioEnc_);
102 int channels;
103 int sampleRate;
104 int64_t bitRate;
105 int sampleFormat;
106 int64_t channelLayout;
107 int frameSize;
108 OH_AVFormat_GetIntValue(fmt, MediaDescriptionKey::MD_KEY_CHANNEL_COUNT.data(), &channels);
109 OH_AVFormat_GetIntValue(fmt, MediaDescriptionKey::MD_KEY_SAMPLE_RATE.data(), &sampleRate);
110 OH_AVFormat_GetLongValue(fmt, MediaDescriptionKey::MD_KEY_BITRATE.data(), &bitRate);
111 OH_AVFormat_GetIntValue(fmt, MediaDescriptionKey::MD_KEY_AUDIO_SAMPLE_FORMAT.data(), &sampleFormat);
112 OH_AVFormat_GetLongValue(fmt, MediaDescriptionKey::MD_KEY_CHANNEL_LAYOUT.data(), &channelLayout);
113 OH_AVFormat_GetIntValue(fmt, MediaDescriptionKey::MD_KEY_AUDIO_SAMPLES_PER_FRAME.data(), &frameSize);
114 std::cout << "GetOutputDescription "
115 << "channels: " << channels
116 << ", sampleRate: " << sampleRate
117 << ", bitRate: " << bitRate
118 << ", sampleFormat: " << sampleFormat
119 << ", channelLayout: " << channelLayout
120 << ", frameSize: " << frameSize << std::endl;
121
122 DEMO_CHECK_AND_RETURN_LOG(Start() == AVCS_ERR_OK, "Fatal: Start fail");
123 auto start = chrono::steady_clock::now();
124
125 {
126 unique_lock<mutex> lock(signal_->startMutex_);
127 signal_->startCond_.wait(lock, [this]() { return (!(isRunning_.load())); });
128 }
129
130 auto end = chrono::steady_clock::now();
131 std::cout << "Encode finished, time = " << std::chrono::duration_cast<chrono::milliseconds>(end - start).count()
132 << " ms" << std::endl;
133 DEMO_CHECK_AND_RETURN_LOG(Stop() == AVCS_ERR_OK, "Fatal: Stop fail");
134 DEMO_CHECK_AND_RETURN_LOG(Release() == AVCS_ERR_OK, "Fatal: Release fail");
135 OH_AVFormat_Destroy(format);
136 }
137
GetFileSize(const std::string & filePath)138 int32_t AEncAvbufferG711muDemo::GetFileSize(const std::string& filePath)
139 {
140 std::ifstream file(filePath, std::ios::binary | std::ios::ate);
141 if (!file) {
142 std::cerr << "Failed to open file: " << filePath << std::endl;
143 return -1;
144 }
145
146 std::streampos fileSize = file.tellg(); // 获取文件大小
147 file.close();
148
149 return (int32_t)fileSize;
150 }
151
AEncAvbufferG711muDemo()152 AEncAvbufferG711muDemo::AEncAvbufferG711muDemo()
153 : isRunning_(false), audioEnc_(nullptr), signal_(nullptr), frameCount_(0)
154 {
155 fileSize_ = GetFileSize(INPUT_FILE_PATH.data());
156 inputFile_ = std::make_unique<std::ifstream>(INPUT_FILE_PATH, std::ios::binary);
157 outputFile_ = std::make_unique<std::ofstream>(OUTPUT_FILE_PATH, std::ios::binary);
158 }
159
~AEncAvbufferG711muDemo()160 AEncAvbufferG711muDemo::~AEncAvbufferG711muDemo()
161 {
162 if (signal_) {
163 delete signal_;
164 signal_ = nullptr;
165 }
166 }
167
CreateEnc()168 int32_t AEncAvbufferG711muDemo::CreateEnc()
169 {
170 audioEnc_ = OH_AudioCodec_CreateByName((AVCodecCodecName::AUDIO_ENCODER_G711MU_NAME).data());
171 DEMO_CHECK_AND_RETURN_RET_LOG(audioEnc_ != nullptr, AVCS_ERR_UNKNOWN, "Fatal: CreateByName fail");
172
173 signal_ = new AEncSignal();
174 DEMO_CHECK_AND_RETURN_RET_LOG(signal_ != nullptr, AVCS_ERR_UNKNOWN, "Fatal: No memory");
175
176 cb_ = {&OnError, &OnOutputFormatChanged, &OnInputBufferAvailable, &OnOutputBufferAvailable};
177 int32_t ret = OH_AudioCodec_RegisterCallback(audioEnc_, cb_, signal_);
178 DEMO_CHECK_AND_RETURN_RET_LOG(ret == AVCS_ERR_OK, AVCS_ERR_UNKNOWN, "Fatal: SetCallback fail");
179
180 return AVCS_ERR_OK;
181 }
182
Configure(OH_AVFormat * format)183 int32_t AEncAvbufferG711muDemo::Configure(OH_AVFormat *format)
184 {
185 return OH_AudioCodec_Configure(audioEnc_, format);
186 }
187
Start()188 int32_t AEncAvbufferG711muDemo::Start()
189 {
190 isRunning_.store(true);
191
192 inputLoop_ = make_unique<thread>(&AEncAvbufferG711muDemo::InputFunc, this);
193 DEMO_CHECK_AND_RETURN_RET_LOG(inputLoop_ != nullptr, AVCS_ERR_UNKNOWN, "Fatal: No memory");
194
195 outputLoop_ = make_unique<thread>(&AEncAvbufferG711muDemo::OutputFunc, this);
196 DEMO_CHECK_AND_RETURN_RET_LOG(outputLoop_ != nullptr, AVCS_ERR_UNKNOWN, "Fatal: No memory");
197
198 return OH_AudioCodec_Start(audioEnc_);
199 }
200
Stop()201 int32_t AEncAvbufferG711muDemo::Stop()
202 {
203 isRunning_.store(false);
204 if (inputLoop_ != nullptr && inputLoop_->joinable()) {
205 {
206 unique_lock<mutex> lock(signal_->inMutex_);
207 signal_->inCond_.notify_all();
208 }
209 inputLoop_->join();
210 inputLoop_ = nullptr;
211 while (!signal_->inQueue_.empty()) {
212 signal_->inQueue_.pop();
213 }
214 while (!signal_->inBufferQueue_.empty()) {
215 signal_->inBufferQueue_.pop();
216 }
217 }
218
219 if (outputLoop_ != nullptr && outputLoop_->joinable()) {
220 {
221 unique_lock<mutex> lock(signal_->outMutex_);
222 signal_->outCond_.notify_all();
223 }
224 outputLoop_->join();
225 outputLoop_ = nullptr;
226 while (!signal_->outQueue_.empty()) {
227 signal_->outQueue_.pop();
228 }
229 while (!signal_->outBufferQueue_.empty()) {
230 signal_->outBufferQueue_.pop();
231 }
232 }
233
234 return OH_AudioCodec_Stop(audioEnc_);
235 }
236
Flush()237 int32_t AEncAvbufferG711muDemo::Flush()
238 {
239 isRunning_.store(false);
240 if (inputLoop_ != nullptr && inputLoop_->joinable()) {
241 {
242 unique_lock<mutex> lock(signal_->inMutex_);
243 signal_->inCond_.notify_all();
244 }
245 inputLoop_->join();
246 inputLoop_ = nullptr;
247 while (!signal_->inQueue_.empty()) {
248 signal_->inQueue_.pop();
249 }
250 while (!signal_->inBufferQueue_.empty()) {
251 signal_->inBufferQueue_.pop();
252 }
253 std::cout << "clear input buffer!\n";
254 }
255
256 if (outputLoop_ != nullptr && outputLoop_->joinable()) {
257 {
258 unique_lock<mutex> lock(signal_->outMutex_);
259 signal_->outCond_.notify_all();
260 }
261 outputLoop_->join();
262 outputLoop_ = nullptr;
263 while (!signal_->outQueue_.empty()) {
264 signal_->outQueue_.pop();
265 }
266 while (!signal_->outBufferQueue_.empty()) {
267 signal_->outBufferQueue_.pop();
268 }
269 std::cout << "clear output buffer!\n";
270 }
271 return OH_AudioCodec_Flush(audioEnc_);
272 }
273
Reset()274 int32_t AEncAvbufferG711muDemo::Reset()
275 {
276 return OH_AudioCodec_Reset(audioEnc_);
277 }
278
Release()279 int32_t AEncAvbufferG711muDemo::Release()
280 {
281 return OH_AudioCodec_Destroy(audioEnc_);
282 }
283
HandleEOS(const uint32_t & index)284 void AEncAvbufferG711muDemo::HandleEOS(const uint32_t& index)
285 {
286 OH_AudioCodec_PushInputBuffer(audioEnc_, index);
287 std::cout << "end buffer\n";
288 signal_->inQueue_.pop();
289 signal_->inBufferQueue_.pop();
290 }
291
InputFunc()292 void AEncAvbufferG711muDemo::InputFunc()
293 {
294 DEMO_CHECK_AND_RETURN_LOG(inputFile_ != nullptr && inputFile_->is_open(), "Fatal: open file fail");
295 while (isRunning_.load()) {
296 if (!isRunning_.load()) {
297 break;
298 }
299 unique_lock<mutex> lock(signal_->inMutex_);
300 signal_->inCond_.wait(lock, [this]() { return (signal_->inQueue_.size() > 0 || !isRunning_.load()); });
301 if (!isRunning_.load()) {
302 break;
303 }
304 uint32_t index = signal_->inQueue_.front();
305 auto buffer = signal_->inBufferQueue_.front();
306 DEMO_CHECK_AND_BREAK_LOG(buffer != nullptr, "Fatal: GetInputBuffer fail");
307 if (!inputFile_->eof()) {
308 inputFile_->read((char*)OH_AVBuffer_GetAddr(buffer), INPUT_FRAME_BYTES);
309 buffer->buffer_->memory_->SetSize(INPUT_FRAME_BYTES);
310 if (inputFile_->gcount() == 0) {
311 buffer->buffer_->memory_->SetSize(1);
312 buffer->buffer_->flag_ = AVCODEC_BUFFER_FLAGS_EOS;
313 HandleEOS(index);
314 break;
315 }
316 } else {
317 buffer->buffer_->memory_->SetSize(1);
318 buffer->buffer_->flag_ = AVCODEC_BUFFER_FLAGS_EOS;
319 HandleEOS(index);
320 break;
321 }
322 DEMO_CHECK_AND_BREAK_LOG(buffer != nullptr, "Fatal: GetInputBuffer fail");
323
324 int32_t ret = AVCS_ERR_OK;
325 if (isFirstFrame_) {
326 buffer->buffer_->flag_ = AVCODEC_BUFFER_FLAGS_CODEC_DATA;
327 ret = OH_AudioCodec_PushInputBuffer(audioEnc_, index);
328 isFirstFrame_ = false;
329 } else {
330 buffer->buffer_->flag_ = AVCODEC_BUFFER_FLAGS_NONE;
331 ret = OH_AudioCodec_PushInputBuffer(audioEnc_, index);
332 }
333 timeStamp_ += FRAME_DURATION_US;
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 AEncAvbufferG711muDemo::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 << "G711mu encode eos" << endl;
388 isRunning_.store(false);
389 signal_->startCond_.notify_all();
390 }
391 }
392 outputFile_->close();
393 }