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 "tester_common.h"
17 #include "native_avcodec_base.h"
18 #include "ui/rs_surface_node.h" // foundation/graphic/graphic_2d/rosen/modules/render_service_client/core/
19 #include "utils.h"
20 #include "hcodec_api.h"
21 #include "hcodec_log.h"
22 #include "type_converter.h"
23 #include "tester_codecbase.h"
24 #include "tester_capi.h"
25
26 namespace OHOS::MediaAVCodec {
27 using namespace std;
28 using namespace OHOS::Rosen;
29
GetNowUs()30 int64_t TesterCommon::GetNowUs()
31 {
32 auto now = chrono::steady_clock::now();
33 return chrono::duration_cast<chrono::microseconds>(now.time_since_epoch()).count();
34 }
35
Run(const CommandOpt & opt)36 bool TesterCommon::Run(const CommandOpt& opt)
37 {
38 opt.Print();
39 shared_ptr<TesterCommon> tester;
40 if (opt.testCodecBaseApi) {
41 tester = make_shared<TesterCodecBase>(opt);
42 } else {
43 tester = make_shared<TesterCapi>(opt);
44 }
45 CostRecorder::Instance().Clear();
46 for (uint32_t i = 0; i < opt.repeatCnt; i++) {
47 printf("i = %u\n", i);
48 bool ret = tester->RunOnce();
49 if (!ret) {
50 return false;
51 }
52 }
53 CostRecorder::Instance().Print();
54 return true;
55 }
56
RunOnce()57 bool TesterCommon::RunOnce()
58 {
59 return opt_.isEncoder ? RunEncoder() : RunDecoder();
60 }
61
OutputLoop()62 void TesterCommon::OutputLoop()
63 {
64 while (true) {
65 optional<uint32_t> outputIdx = GetOutputIndex();
66 if (!outputIdx.has_value()) {
67 return;
68 }
69 ReturnOutput(outputIdx.value());
70 }
71 }
72
RunEncoder()73 bool TesterCommon::RunEncoder()
74 {
75 ifs_ = ifstream(opt_.inputFile, ios::binary);
76 IF_TRUE_RETURN_VAL_WITH_MSG(!ifs_, false, "Failed to open file %{public}s", opt_.inputFile.c_str());
77 optional<GraphicPixelFormat> displayFmt = TypeConverter::InnerFmtToDisplayFmt(opt_.pixFmt);
78 IF_TRUE_RETURN_VAL_WITH_MSG(!displayFmt, false, "invalid pixel format");
79 displayFmt_ = displayFmt.value();
80
81 bool ret = Create();
82 IF_TRUE_RETURN_VAL(!ret, false);
83 ret = SetCallback();
84 IF_TRUE_RETURN_VAL(!ret, false);
85 ret = ConfigureEncoder();
86 IF_TRUE_RETURN_VAL(!ret, false);
87 ret = GetInputFormat();
88 if (opt_.isBufferMode) {
89 stride_ = opt_.dispW;
90 if (ret) {
91 optional<uint32_t> stride = GetInputStride();
92 if (stride && stride.value() >= opt_.dispW) {
93 stride_ = stride.value();
94 }
95 }
96 } else {
97 ret = CreateInputSurface();
98 IF_TRUE_RETURN_VAL(!ret, false);
99 }
100 GetOutputFormat();
101 ret = Start();
102 IF_TRUE_RETURN_VAL(!ret, false);
103
104 thread th(&TesterCommon::OutputLoop, this);
105 if (opt_.isBufferMode) {
106 EncoderInputLoop();
107 } else {
108 InputSurfaceLoop();
109 }
110 if (th.joinable()) {
111 th.join();
112 }
113 ret = Stop();
114 IF_TRUE_RETURN_VAL(!ret, false);
115 ret = Release();
116 IF_TRUE_RETURN_VAL(!ret, false);
117 return true;
118 }
119
EncoderInputLoop()120 void TesterCommon::EncoderInputLoop()
121 {
122 while (true) {
123 Span span;
124 optional<uint32_t> inputIdx = GetInputIndex(span);
125 if (!inputIdx.has_value()) {
126 continue;
127 }
128 OH_AVCodecBufferAttr info;
129 info.offset = 0;
130 info.flags = 0;
131 info.size = ReadOneFrame(span);
132 if (info.size == 0 || (opt_.inputCnt > 0 && currInputCnt_ > opt_.inputCnt)) {
133 info.flags = AVCODEC_BUFFER_FLAGS_EOS;
134 if (!QueueInput(inputIdx.value(), info)) {
135 continue;
136 } else {
137 LOGI("queue eos succ, quit loop");
138 return;
139 }
140 }
141 info.pts = GetNowUs();
142 if (!QueueInput(inputIdx.value(), info)) {
143 continue;
144 }
145 currInputCnt_++;
146 if (currInputCnt_ == opt_.numIdrFrame) {
147 RequestIDR();
148 }
149 }
150 }
151
InputSurfaceLoop()152 void TesterCommon::InputSurfaceLoop()
153 {
154 BufferRequestConfig cfg = {opt_.dispW, opt_.dispH, 32, displayFmt_,
155 BUFFER_USAGE_CPU_READ | BUFFER_USAGE_CPU_WRITE | BUFFER_USAGE_MEM_DMA, 0, };
156
157 while (true) {
158 sptr<SurfaceBuffer> surfaceBuffer;
159 int32_t fence;
160 GSError err = surface_->RequestBuffer(surfaceBuffer, fence, cfg);
161 if (err != GSERROR_OK || surfaceBuffer == nullptr) {
162 this_thread::sleep_for(10ms);
163 continue;
164 }
165 char* dst = reinterpret_cast<char *>(surfaceBuffer->GetVirAddr());
166 int stride = surfaceBuffer->GetStride();
167 if (dst == nullptr || stride < opt_.dispW) {
168 LOGE("invalid va or stride %{public}d", stride);
169 surface_->CancelBuffer(surfaceBuffer);
170 continue;
171 }
172 stride_ = stride;
173 uint32_t filledLen = ReadOneFrame(Span {dst, surfaceBuffer->GetSize()});
174 if (filledLen == 0 || (opt_.inputCnt > 0 && currInputCnt_ > opt_.inputCnt)) {
175 LOGI("input eos, quit loop");
176 if (!NotifyEos()) {
177 LOGE("NotifyEos failed");
178 }
179 return;
180 }
181 BufferFlushConfig flushConfig = {
182 .damage = {
183 .w = opt_.dispW,
184 .h = opt_.dispH,
185 },
186 .timestamp = GetNowUs(),
187 };
188 err = surface_->FlushBuffer(surfaceBuffer, -1, flushConfig);
189 if (err != GSERROR_OK) {
190 LOGE("FlushBuffer failed");
191 continue;
192 }
193 currInputCnt_++;
194 if (currInputCnt_ == opt_.numIdrFrame) {
195 RequestIDR();
196 }
197 }
198 }
199
200 #define RETURN_ZERO_IF_EOS(expectedSize) \
201 do { \
202 if (ifs_.gcount() != (expectedSize)) { \
203 LOGI("no more data"); \
204 return 0; \
205 } \
206 } while (0)
207
ReadOneFrameYUV420P(char * dst)208 uint32_t TesterCommon::ReadOneFrameYUV420P(char* dst)
209 {
210 char* start = dst;
211 // copy Y
212 for (uint32_t i = 0; i < opt_.dispH; i++) {
213 ifs_.read(dst, opt_.dispW);
214 RETURN_ZERO_IF_EOS(opt_.dispW);
215 dst += stride_;
216 }
217 // copy U
218 for (uint32_t i = 0; i < opt_.dispH / SAMPLE_RATIO; i++) {
219 ifs_.read(dst, opt_.dispW / SAMPLE_RATIO);
220 RETURN_ZERO_IF_EOS(opt_.dispW / SAMPLE_RATIO);
221 dst += stride_ / SAMPLE_RATIO;
222 }
223 // copy V
224 for (uint32_t i = 0; i < opt_.dispH / SAMPLE_RATIO; i++) {
225 ifs_.read(dst, opt_.dispW / SAMPLE_RATIO);
226 RETURN_ZERO_IF_EOS(opt_.dispW / SAMPLE_RATIO);
227 dst += stride_ / SAMPLE_RATIO;
228 }
229 return dst - start;
230 }
231
ReadOneFrameYUV420SP(char * dst)232 uint32_t TesterCommon::ReadOneFrameYUV420SP(char* dst)
233 {
234 char* start = dst;
235 // copy Y
236 for (uint32_t i = 0; i < opt_.dispH; i++) {
237 ifs_.read(dst, opt_.dispW);
238 RETURN_ZERO_IF_EOS(opt_.dispW);
239 dst += stride_;
240 }
241 // copy UV
242 for (uint32_t i = 0; i < opt_.dispH / SAMPLE_RATIO; i++) {
243 ifs_.read(dst, opt_.dispW);
244 RETURN_ZERO_IF_EOS(opt_.dispW);
245 dst += stride_;
246 }
247 return dst - start;
248 }
249
ReadOneFrameRGBA(char * dst)250 uint32_t TesterCommon::ReadOneFrameRGBA(char* dst)
251 {
252 char* start = dst;
253 for (uint32_t i = 0; i < opt_.dispH; i++) {
254 ifs_.read(dst, opt_.dispW * BYTES_PER_PIXEL_RBGA);
255 RETURN_ZERO_IF_EOS(opt_.dispW * BYTES_PER_PIXEL_RBGA);
256 dst += stride_;
257 }
258 return dst - start;
259 }
260
ReadOneFrame(Span dstSpan)261 uint32_t TesterCommon::ReadOneFrame(Span dstSpan)
262 {
263 uint32_t sampleSize = 0;
264 switch (opt_.pixFmt) {
265 case YUVI420:
266 case NV12:
267 case NV21: {
268 sampleSize = GetYuv420Size(stride_, opt_.dispH);
269 break;
270 }
271 case RGBA: {
272 sampleSize = stride_ * opt_.dispH;
273 break;
274 }
275 default:
276 return 0;
277 }
278 if (sampleSize > dstSpan.capacity) {
279 LOGE("sampleSize %{public}u > dst capacity %{public}zu", sampleSize, dstSpan.capacity);
280 return 0;
281 }
282
283 switch (opt_.pixFmt) {
284 case YUVI420: {
285 return ReadOneFrameYUV420P(dstSpan.va);
286 }
287 case NV12:
288 case NV21: {
289 return ReadOneFrameYUV420SP(dstSpan.va);
290 }
291 case RGBA: {
292 return ReadOneFrameRGBA(dstSpan.va);
293 }
294 default:
295 return 0;
296 }
297 }
298
RunDecoder()299 bool TesterCommon::RunDecoder()
300 {
301 ifs_ = ifstream(opt_.inputFile, ios::binary);
302 IF_TRUE_RETURN_VAL_WITH_MSG(!ifs_, false, "Failed to open file %{public}s", opt_.inputFile.c_str());
303 totalSampleCnt_ = demuxer_.SetSource(opt_.inputFile, opt_.protocol);
304 if (totalSampleCnt_ == 0) {
305 LOGE("no nalu found");
306 return false;
307 }
308
309 bool ret = Create();
310 IF_TRUE_RETURN_VAL(!ret, false);
311 ret = SetCallback();
312 IF_TRUE_RETURN_VAL(!ret, false);
313 ret = ConfigureDecoder();
314 IF_TRUE_RETURN_VAL(!ret, false);
315 if (!opt_.isBufferMode) {
316 sptr<Surface> surface = CreateSurfaceNormal();
317 if (surface == nullptr) {
318 return false;
319 }
320 ret = SetOutputSurface(surface);
321 IF_TRUE_RETURN_VAL(!ret, false);
322 }
323 GetInputFormat();
324 GetOutputFormat();
325 ret = Start();
326 IF_TRUE_RETURN_VAL(!ret, false);
327
328 thread outputThread(&TesterCommon::OutputLoop, this);
329 DecoderInputLoop();
330 if (outputThread.joinable()) {
331 outputThread.join();
332 }
333
334 ret = Stop();
335 IF_TRUE_RETURN_VAL(!ret, false);
336 ret = Release();
337 IF_TRUE_RETURN_VAL(!ret, false);
338 if (window_ != nullptr) {
339 window_->Destroy();
340 window_ = nullptr;
341 }
342 return true;
343 }
344
CreateSurfaceFromWindow()345 sptr<Surface> TesterCommon::CreateSurfaceFromWindow()
346 {
347 sptr<WindowOption> option = new WindowOption();
348 option->SetWindowRect({ 0, 0, opt_.dispW, opt_.dispH });
349 option->SetWindowType(WindowType::WINDOW_TYPE_FLOAT);
350 option->SetWindowMode(WindowMode::WINDOW_MODE_FLOATING);
351 sptr<Window> window = Window::Create("DemoWindow", option);
352 if (window == nullptr) {
353 LOGE("Create Window failed");
354 return nullptr;
355 }
356 shared_ptr<RSSurfaceNode> node = window->GetSurfaceNode();
357 if (node == nullptr) {
358 LOGE("GetSurfaceNode failed");
359 return nullptr;
360 }
361 sptr<Surface> surface = node->GetSurface();
362 if (surface == nullptr) {
363 LOGE("GetSurface failed");
364 return nullptr;
365 }
366 window->Show();
367 window_ = window;
368 return surface;
369 }
370
CreateSurfaceNormal()371 sptr<Surface> TesterCommon::CreateSurfaceNormal()
372 {
373 sptr<Surface> consumerSurface = Surface::CreateSurfaceAsConsumer();
374 if (consumerSurface == nullptr) {
375 LOGE("CreateSurfaceAsConsumer failed");
376 return nullptr;
377 }
378 sptr<IBufferConsumerListener> listener = new Listener(this);
379 GSError err = consumerSurface->RegisterConsumerListener(listener);
380 if (err != GSERROR_OK) {
381 LOGE("RegisterConsumerListener failed");
382 return nullptr;
383 }
384 sptr<IBufferProducer> bufferProducer = consumerSurface->GetProducer();
385 sptr<Surface> producerSurface = Surface::CreateSurfaceAsProducer(bufferProducer);
386 if (producerSurface == nullptr) {
387 LOGE("CreateSurfaceAsProducer failed");
388 return nullptr;
389 }
390 surface_ = consumerSurface;
391 return producerSurface;
392 }
393
OnBufferAvailable()394 void TesterCommon::Listener::OnBufferAvailable()
395 {
396 sptr<SurfaceBuffer> buffer;
397 int32_t fence;
398 int64_t timestamp;
399 OHOS::Rect damage;
400 GSError err = tester_->surface_->AcquireBuffer(buffer, fence, timestamp, damage);
401 if (err != GSERROR_OK || buffer == nullptr) {
402 LOGW("AcquireBuffer failed");
403 return;
404 }
405 tester_->surface_->ReleaseBuffer(buffer, -1);
406 }
407
DecoderInputLoop()408 void TesterCommon::DecoderInputLoop()
409 {
410 PrepareSeek();
411 while (true) {
412 if (!SeekIfNecessary()) {
413 return;
414 }
415 Span span;
416 optional<uint32_t> inputIdx = GetInputIndex(span);
417 if (!inputIdx.has_value()) {
418 continue;
419 }
420 OH_AVCodecBufferAttr info;
421 info.offset = 0;
422 info.flags = 0;
423
424 size_t sampleIdx;
425 bool isCsd;
426 info.size = GetNextSample(span, sampleIdx, isCsd);
427 if (isCsd) {
428 info.flags = AVCODEC_BUFFER_FLAGS_CODEC_DATA;
429 }
430 if (info.size == 0 || (opt_.inputCnt > 0 && currInputCnt_ > opt_.inputCnt)) {
431 info.flags = AVCODEC_BUFFER_FLAGS_EOS;
432 if (!QueueInput(inputIdx.value(), info)) {
433 LOGW("queue eos failed");
434 continue;
435 } else {
436 LOGI("queue eos succ, quit loop");
437 return;
438 }
439 }
440 info.pts = GetNowUs();
441 if (!QueueInput(inputIdx.value(), info)) {
442 LOGW("queue sample %{public}zu failed", sampleIdx);
443 continue;
444 }
445 currInputCnt_++;
446 currSampleIdx_ = sampleIdx;
447 demuxer_.MoveToNext();
448 }
449 }
450
GenerateRandomNumInRange(size_t rangeStart,size_t rangeEnd)451 static size_t GenerateRandomNumInRange(size_t rangeStart, size_t rangeEnd)
452 {
453 return rangeStart + rand() % (rangeEnd - rangeStart);
454 }
455
PrepareSeek()456 void TesterCommon::PrepareSeek()
457 {
458 int mockCnt = 0;
459 size_t lastSeekTo = 0;
460 while (mockCnt++ < opt_.flushCnt) {
461 size_t seekFrom = GenerateRandomNumInRange(lastSeekTo, totalSampleCnt_);
462 size_t seekTo = GenerateRandomNumInRange(0, totalSampleCnt_);
463 LOGI("mock seek from sample index %{public}zu to %{public}zu", seekFrom, seekTo);
464 userSeekPos_.emplace_back(seekFrom, seekTo);
465 lastSeekTo = seekTo;
466 }
467 }
468
SeekIfNecessary()469 bool TesterCommon::SeekIfNecessary()
470 {
471 if (userSeekPos_.empty()) {
472 return true;
473 }
474 size_t seekFrom;
475 size_t seekTo;
476 std::tie(seekFrom, seekTo) = userSeekPos_.front();
477 if (currSampleIdx_ != seekFrom) {
478 return true;
479 }
480 LOGI("begin to seek from sample index %{public}zu to %{public}zu", seekFrom, seekTo);
481 if (!demuxer_.SeekTo(seekTo)) {
482 return true;
483 }
484 if (!Flush()) {
485 return false;
486 }
487 if (!Start()) {
488 return false;
489 }
490 userSeekPos_.pop_front();
491 return true;
492 }
493
GetNextSample(Span dstSpan,size_t & sampleIdx,bool & isCsd)494 int TesterCommon::GetNextSample(Span dstSpan, size_t& sampleIdx, bool& isCsd)
495 {
496 optional<Sample> sample = demuxer_.PeekNextSample();
497 if (!sample.has_value()) {
498 return 0;
499 }
500 uint32_t sampleSize = sample->endPos - sample->startPos;
501 if (sampleSize > dstSpan.capacity) {
502 LOGE("sampleSize %{public}u > dst capacity %{public}zu", sampleSize, dstSpan.capacity);
503 return 0;
504 }
505 sampleIdx = sample->idx;
506 isCsd = sample->isCsd;
507 ifs_.seekg(sample->startPos);
508 ifs_.read(dstSpan.va, sampleSize);
509 return sampleSize;
510 }
511 } // namespace OHOS::MediaAVCodec