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 <iostream>
17 #include <unistd.h>
18 #include "avcodec_codec_name.h"
19 #include "avcodec_common.h"
20 #include "avcodec_errors.h"
21 #include "iconsumer_surface.h"
22 #include "demo_log.h"
23 #include "media_description.h"
24 #include "securec.h"
25 #include "ui/rs_surface_node.h"
26 #include "wm/window.h"
27 #include "window_option.h"
28 #include "avcodec_video_decoder_inner_demo.h"
29
30
31 using namespace OHOS;
32 using namespace OHOS::MediaAVCodec;
33 using namespace OHOS::MediaAVCodec::InnerVideoDemo;
34 using namespace std;
35 namespace {
36 constexpr uint32_t DEFAULT_WIDTH = 320;
37 constexpr uint32_t DEFAULT_HEIGHT = 240;
38
39 constexpr string_view inputFilePath = "/data/test/media/out_320_240_10s.h264";
40 constexpr string_view outputFilePath = "/data/test/media/out_320_240_10s.yuv";
41 constexpr string_view outputSurfacePath = "/data/test/media/out_320_240_10s.rgba";
42 uint32_t outFrameCount = 0;
43 constexpr uint32_t SLEEP_TIME = 1;
44 } // namespace
45
TestConsumerListener(sptr<Surface> cs,std::string_view name)46 TestConsumerListener::TestConsumerListener(sptr<Surface> cs, std::string_view name) : cs_(cs)
47 {
48 outFile_ = std::make_unique<std::ofstream>();
49 outFile_->open(name.data(), std::ios::out | std::ios::binary);
50 }
51
~TestConsumerListener()52 TestConsumerListener::~TestConsumerListener()
53 {
54 if (outFile_ != nullptr) {
55 outFile_->close();
56 }
57 }
58
OnBufferAvailable()59 void TestConsumerListener::OnBufferAvailable()
60 {
61 sptr<SurfaceBuffer> buffer;
62 int32_t flushFence;
63
64 cs_->AcquireBuffer(buffer, flushFence, timestamp_, damage_);
65
66 (void)outFile_->write(reinterpret_cast<char *>(buffer->GetVirAddr()), buffer->GetSize());
67 cs_->ReleaseBuffer(buffer, -1);
68 }
69
RunCase(std::string & mode)70 void VDecInnerDemo::RunCase(std::string &mode)
71 {
72 mode_ = mode;
73
74 DEMO_CHECK_AND_RETURN_LOG(CreateDec() == AVCS_ERR_OK, "Fatal: CreateDec fail");
75
76 Format format;
77 format.PutIntValue(MediaDescriptionKey::MD_KEY_WIDTH, DEFAULT_WIDTH);
78 format.PutIntValue(MediaDescriptionKey::MD_KEY_HEIGHT, DEFAULT_HEIGHT);
79 DEMO_CHECK_AND_RETURN_LOG(Configure(format) == AVCS_ERR_OK, "Fatal: Configure fail");
80
81 if (mode_ != "0") {
82 sptr<Surface> ps = GetSurface(mode_);
83 DEMO_CHECK_AND_RETURN_LOG(SetOutputSurface(ps) == AVCS_ERR_OK, "Fatal: SetSurface fail");
84 }
85
86 DEMO_CHECK_AND_RETURN_LOG(Start() == AVCS_ERR_OK, "Fatal: Start fail");
87 while (isRunning_.load()) {
88 sleep(SLEEP_TIME); // start run 1s
89 }
90 DEMO_CHECK_AND_RETURN_LOG(Stop() == AVCS_ERR_OK, "Fatal: Stop fail");
91 DEMO_CHECK_AND_RETURN_LOG(Release() == AVCS_ERR_OK, "Fatal: Release fail");
92 }
93
VDecInnerDemo()94 VDecInnerDemo::VDecInnerDemo()
95 {
96 codec_ = avcodec_find_decoder(AV_CODEC_ID_H264);
97 if (codec_ == nullptr) {
98 std::cout << "find h264 fail" << std::endl;
99 exit(1);
100 }
101
102 parser_ = av_parser_init(codec_->id);
103 if (parser_ == nullptr) {
104 std::cout << "parser init fail" << std::endl;
105 exit(1);
106 }
107
108 codec_ctx_ = avcodec_alloc_context3(codec_);
109 if (codec_ctx_ == nullptr) {
110 std::cout << "alloc context fail" << std::endl;
111 exit(1);
112 }
113
114 if (avcodec_open2(codec_ctx_, codec_, NULL) < 0) {
115 std::cout << "codec open fail" << std::endl;
116 exit(1);
117 }
118
119 pkt_ = av_packet_alloc();
120 if (pkt_ == nullptr) {
121 std::cout << "alloc pkt_ fail" << std::endl;
122 exit(1);
123 }
124 pkt_->data = NULL;
125 pkt_->size = 0;
126 }
127
~VDecInnerDemo()128 VDecInnerDemo::~VDecInnerDemo()
129 {
130 avcodec_free_context(&codec_ctx_);
131 av_parser_close(parser_);
132 av_packet_free(&pkt_);
133
134 if (inputFile_ != nullptr) {
135 inputFile_->close();
136 }
137
138 if (outFile_ != nullptr) {
139 outFile_->close();
140 }
141 }
142
GetSurface(std::string & mode)143 sptr<Surface> VDecInnerDemo::GetSurface(std::string &mode)
144 {
145 sptr<Surface> ps = nullptr;
146 if (mode == "1") {
147 sptr<Surface> cs = Surface::CreateSurfaceAsConsumer();
148 sptr<IBufferConsumerListener> listener = new InnerVideoDemo::TestConsumerListener(cs, outputSurfacePath);
149 cs->RegisterConsumerListener(listener);
150 auto p = cs->GetProducer();
151 ps = Surface::CreateSurfaceAsProducer(p);
152 } else if (mode == "2") {
153 sptr<Rosen::Window> window = nullptr;
154 sptr<Rosen::WindowOption> option = new Rosen::WindowOption();
155 option->SetWindowRect({0, 0, DEFAULT_WIDTH, DEFAULT_HEIGHT});
156 option->SetWindowType(Rosen::WindowType::WINDOW_TYPE_FLOAT);
157 option->SetWindowMode(Rosen::WindowMode::WINDOW_MODE_FLOATING);
158 window = Rosen::Window::Create("avcodec_unittest", option);
159 DEMO_CHECK_AND_RETURN_RET_LOG(window != nullptr && window->GetSurfaceNode() != nullptr, nullptr,
160 "Fatal: Create window fail");
161 window->Show();
162 ps = window->GetSurfaceNode()->GetSurface();
163 }
164
165 return ps;
166 }
167
CreateDec()168 int32_t VDecInnerDemo::CreateDec()
169 {
170 videoDec_ = VideoDecoderFactory::CreateByName(AVCodecCodecName::VIDEO_DECODER_AVC_NAME.data());
171 DEMO_CHECK_AND_RETURN_RET_LOG(videoDec_ != nullptr, AVCS_ERR_UNKNOWN, "Fatal: CreateByName fail");
172
173 signal_ = make_shared<VDecSignal>();
174
175 cb_ = make_unique<VDecDemoCallback>(signal_);
176 DEMO_CHECK_AND_RETURN_RET_LOG(cb_ != nullptr, AVCS_ERR_UNKNOWN, "Fatal: No memory");
177 DEMO_CHECK_AND_RETURN_RET_LOG(videoDec_->SetCallback(cb_) == AVCS_ERR_OK, AVCS_ERR_UNKNOWN,
178 "Fatal: SetCallback fail");
179
180 return AVCS_ERR_OK;
181 }
182
Configure(const Format & format)183 int32_t VDecInnerDemo::Configure(const Format &format)
184 {
185 return videoDec_->Configure(format);
186 }
187
SetOutputSurface(sptr<Surface> surface)188 int32_t VDecInnerDemo::SetOutputSurface(sptr<Surface> surface)
189 {
190 return videoDec_->SetOutputSurface(surface);
191 }
192
Start()193 int32_t VDecInnerDemo::Start()
194 {
195 inputFile_ = std::make_unique<std::ifstream>();
196 DEMO_CHECK_AND_RETURN_RET_LOG(inputFile_ != nullptr, AVCS_ERR_UNKNOWN, "Fatal: No memory");
197 inputFile_->open(inputFilePath.data(), std::ios::in | std::ios::binary);
198
199 if (mode_ == "0") {
200 outFile_ = std::make_unique<std::ofstream>();
201 DEMO_CHECK_AND_RETURN_RET_LOG(outFile_ != nullptr, AVCS_ERR_UNKNOWN, "Fatal: No memory");
202 outFile_->open(outputFilePath.data(), std::ios::out | std::ios::binary);
203 }
204
205 isRunning_.store(true);
206
207 inputLoop_ = make_unique<thread>(&VDecInnerDemo::InputFunc, this);
208 DEMO_CHECK_AND_RETURN_RET_LOG(inputLoop_ != nullptr, AVCS_ERR_UNKNOWN, "Fatal: No memory");
209
210 outputLoop_ = make_unique<thread>(&VDecInnerDemo::OutputFunc, this);
211 DEMO_CHECK_AND_RETURN_RET_LOG(outputLoop_ != nullptr, AVCS_ERR_UNKNOWN, "Fatal: No memory");
212
213 return videoDec_->Start();
214 }
215
Stop()216 int32_t VDecInnerDemo::Stop()
217 {
218 isRunning_.store(false);
219
220 if (inputLoop_ != nullptr && inputLoop_->joinable()) {
221 unique_lock<mutex> lock(signal_->inMutex_);
222 signal_->inQueue_.push(0);
223 signal_->inCond_.notify_all();
224 lock.unlock();
225 inputLoop_->join();
226 inputLoop_.reset();
227 }
228
229 if (outputLoop_ != nullptr && outputLoop_->joinable()) {
230 unique_lock<mutex> lock(signal_->outMutex_);
231 signal_->outQueue_.push(0);
232 signal_->outCond_.notify_all();
233 lock.unlock();
234 outputLoop_->join();
235 outputLoop_.reset();
236 }
237
238 return videoDec_->Stop();
239 }
240
Flush()241 int32_t VDecInnerDemo::Flush()
242 {
243 return videoDec_->Flush();
244 }
245
Reset()246 int32_t VDecInnerDemo::Reset()
247 {
248 return videoDec_->Reset();
249 }
250
Release()251 int32_t VDecInnerDemo::Release()
252 {
253 return videoDec_->Release();
254 }
255
HandleInputEOS(const uint32_t & index)256 void VDecInnerDemo::HandleInputEOS(const uint32_t &index)
257 {
258 AVCodecBufferInfo attr;
259 AVCodecBufferFlag flag;
260 attr.presentationTimeUs = 0;
261 attr.size = 0;
262 attr.offset = 0;
263 flag = AVCodecBufferFlag::AVCODEC_BUFFER_FLAG_EOS;
264 (void)videoDec_->QueueInputBuffer(index, attr, flag);
265 std::cout << "end buffer" << std::endl;
266 }
267
HandleNormalInput(const uint32_t & index,const int64_t & pts,const size_t & size)268 int32_t VDecInnerDemo::HandleNormalInput(const uint32_t &index, const int64_t &pts, const size_t &size)
269 {
270 AVCodecBufferInfo attr;
271 AVCodecBufferFlag flag;
272 attr.presentationTimeUs = pts;
273 attr.size = size;
274 attr.offset = 0;
275 flag = AVCodecBufferFlag::AVCODEC_BUFFER_FLAG_NONE;
276 auto result = videoDec_->QueueInputBuffer(index, attr, flag);
277 return result;
278 }
279
ExtractPacket()280 int32_t VDecInnerDemo::ExtractPacket()
281 {
282 int32_t len = 0;
283 int32_t ret = 0;
284
285 if (data_ == nullptr) {
286 data_ = inbuf_;
287 (void)inputFile_->read(reinterpret_cast<char *>(inbuf_), VIDEO_INBUF_SIZE);
288 data_size_ = inputFile_->gcount();
289 }
290
291 if ((data_size_ < VIDEO_REFILL_THRESH) && !file_end_) {
292 memmove_s(inbuf_, data_size_, data_, data_size_);
293 data_ = inbuf_;
294 (void)inputFile_->read(reinterpret_cast<char *>(data_ + data_size_), VIDEO_INBUF_SIZE - data_size_);
295 len = inputFile_->gcount();
296 if (len > 0) {
297 data_size_ += len;
298 } else if (len == 0 && data_size_ == 0) {
299 file_end_ = true;
300 cout << "extract file end" << endl;
301 }
302 }
303
304 if (data_size_ > 0) {
305 ret = av_parser_parse2(parser_, codec_ctx_, &pkt_->data, &pkt_->size, data_, data_size_, AV_NOPTS_VALUE,
306 AV_NOPTS_VALUE, 0);
307 if (ret < 0) {
308 cout << "av_parser_parser2 Error!" << endl;
309 }
310 data_ += ret;
311 data_size_ -= ret;
312 if (pkt_->size) {
313 return AVCS_ERR_OK;
314 } else {
315 return AVCS_ERR_UNKNOWN;
316 }
317 }
318 return AVCS_ERR_UNKNOWN;
319 }
320
InputFunc()321 void VDecInnerDemo::InputFunc()
322 {
323 while (isRunning_.load()) {
324 std::unique_lock<std::mutex> lock(signal_->inMutex_);
325 signal_->inCond_.wait(lock, [this]() { return signal_->inQueue_.size() > 0; });
326 if (!isRunning_.load()) {
327 break;
328 }
329 uint32_t index = signal_->inQueue_.front();
330 std::shared_ptr<AVSharedMemory> buffer = signal_->inBufferQueue_.front();
331 lock.unlock();
332
333 if (buffer == nullptr) {
334 isRunning_.store(false);
335 std::cout << "buffer is null:" << index << std::endl;
336 break;
337 }
338
339 if (!file_end_ && (ExtractPacket() != AVCS_ERR_OK || pkt_->size == 0)) {
340 continue;
341 }
342
343 if (file_end_) {
344 HandleInputEOS(index);
345 av_packet_unref(pkt_);
346 break;
347 }
348
349 if (memcpy_s(buffer->GetBase(), buffer->GetSize(), pkt_->data, pkt_->size) != EOK) {
350 cout << "Fatal: memcpy fail" << endl;
351 break;
352 }
353 auto result = HandleNormalInput(index, pkt_->pts, pkt_->size);
354 av_packet_unref(pkt_);
355
356 if (result != AVCS_ERR_OK) {
357 std::cout << "QueueInputBuffer error:\n";
358 isRunning_ = false;
359 break;
360 }
361 lock.lock();
362 signal_->inQueue_.pop();
363 signal_->inBufferQueue_.pop();
364 }
365 }
366
OutputFunc()367 void VDecInnerDemo::OutputFunc()
368 {
369 while (isRunning_.load()) {
370 unique_lock<mutex> lock(signal_->outMutex_);
371 signal_->outCond_.wait(lock, [this]() { return signal_->outQueue_.size() > 0; });
372 if (!isRunning_.load()) {
373 break;
374 }
375
376 uint32_t index = signal_->outQueue_.front();
377 auto attr = signal_->infoQueue_.front();
378 auto flag = signal_->flagQueue_.front();
379 auto buffer = signal_->outBufferQueue_.front();
380 lock.unlock();
381 if (flag == AVCODEC_BUFFER_FLAG_EOS) {
382 cout << "decode eos, write frame:" << outFrameCount << endl;
383 isRunning_.store(false);
384 }
385
386 if (outFile_ != nullptr && attr.size != 0 && buffer != nullptr && buffer->GetBase() != nullptr) {
387 cout << "OutputFunc write file,buffer index" << index << ", data size = :" << attr.size << endl;
388 outFile_->write(reinterpret_cast<char *>(buffer->GetBase()), attr.size);
389 }
390
391 if (mode_ == "0" && videoDec_->ReleaseOutputBuffer(index, false) != AVCS_ERR_OK) {
392 cout << "Fatal: ReleaseOutputBuffer fail" << endl;
393 break;
394 }
395
396 if (mode_ != "0" && videoDec_->ReleaseOutputBuffer(index, true) != AVCS_ERR_OK) {
397 cout << "Fatal: RenderOutputBuffer fail" << endl;
398 break;
399 }
400 lock.lock();
401 signal_->outQueue_.pop();
402 signal_->infoQueue_.pop();
403 signal_->flagQueue_.pop();
404 signal_->outBufferQueue_.pop();
405 }
406 }
407
VDecDemoCallback(shared_ptr<VDecSignal> signal)408 VDecDemoCallback::VDecDemoCallback(shared_ptr<VDecSignal> signal) : signal_(signal) {}
409
OnError(AVCodecErrorType errorType,int32_t errorCode)410 void VDecDemoCallback::OnError(AVCodecErrorType errorType, int32_t errorCode)
411 {
412 cout << "Error received, errorType:" << errorType << " errorCode:" << errorCode << endl;
413 }
414
OnOutputFormatChanged(const Format & format)415 void VDecDemoCallback::OnOutputFormatChanged(const Format &format)
416 {
417 (void)format;
418 cout << "OnOutputFormatChanged received" << endl;
419 }
420
OnInputBufferAvailable(uint32_t index,std::shared_ptr<AVSharedMemory> buffer)421 void VDecDemoCallback::OnInputBufferAvailable(uint32_t index, std::shared_ptr<AVSharedMemory> buffer)
422 {
423 cout << "OnInputBufferAvailable received, index:" << index << endl;
424 unique_lock<mutex> lock(signal_->inMutex_);
425 signal_->inQueue_.push(index);
426 signal_->inBufferQueue_.push(buffer);
427 signal_->inCond_.notify_all();
428 }
429
OnOutputBufferAvailable(uint32_t index,AVCodecBufferInfo info,AVCodecBufferFlag flag,std::shared_ptr<AVSharedMemory> buffer)430 void VDecDemoCallback::OnOutputBufferAvailable(uint32_t index, AVCodecBufferInfo info, AVCodecBufferFlag flag,
431 std::shared_ptr<AVSharedMemory> buffer)
432 {
433 (void)info;
434 (void)flag;
435 cout << "OnOutputBufferAvailable received, index:" << index << endl;
436 unique_lock<mutex> lock(signal_->outMutex_);
437 signal_->outQueue_.push(index);
438 signal_->outBufferQueue_.push(buffer);
439 signal_->infoQueue_.push(info);
440 signal_->flagQueue_.push(flag);
441 if (info.size > 0) {
442 outFrameCount++;
443 }
444 signal_->outCond_.notify_all();
445 }