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 }