1 /*
2  * Copyright (C) 2024 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 "video_sample_base.h"
17 #include <chrono>
18 #include "external_window.h"
19 #include "av_codec_sample_log.h"
20 #include "av_codec_sample_error.h"
21 #include "sample_helper.h"
22 
23 namespace {
24 using namespace std::string_literals;
25 using namespace std::chrono_literals;
26 constexpr OHOS::HiviewDFX::HiLogLabel LABEL = {LOG_CORE, LOG_DOMAIN_TEST, "VideoSampleBase"};
27 }
28 
29 namespace OHOS {
30 namespace MediaAVCodec {
31 namespace Sample {
~VideoSampleBase()32 VideoSampleBase::~VideoSampleBase()
33 {
34     InnerRelease();
35 }
36 
Create(SampleInfo sampleInfo)37 int32_t VideoSampleBase::Create(SampleInfo sampleInfo)
38 {
39     std::lock_guard<std::mutex> lock(mutex_);
40     CHECK_AND_RETURN_RET_LOG(context_ == nullptr, AVCODEC_SAMPLE_ERR_ERROR, "Already started.");
41 
42     context_ = std::make_shared<SampleContext>();
43     context_->sampleInfo = std::make_shared<SampleInfo>(sampleInfo);
44     auto &info = *context_->sampleInfo;
45     auto &videoCodec = context_->videoCodec;
46 
47     dataProducer_ = DataProducerFactory::CreateDataProducer(info.dataProducerInfo.dataProducerType);
48     CHECK_AND_RETURN_RET_LOG(dataProducer_ != nullptr, AVCODEC_SAMPLE_ERR_ERROR, "Create data producer failed");
49     int32_t ret = dataProducer_->Init(context_->sampleInfo);
50     CHECK_AND_RETURN_RET_LOG(ret == AVCODEC_SAMPLE_ERR_OK, ret, "Data producer init failed");
51 
52     videoCodec = VideoCodecFactory::CreateVideoCodec(info.codecType, info.codecRunMode);
53     CHECK_AND_RETURN_RET_LOG(videoCodec != nullptr, AVCODEC_SAMPLE_ERR_ERROR,
54         "Create video encoder failed, no memory");
55     ret = videoCodec->Create(info.codecMime, info.codecType & 0b1);  // 0b1: software codec mask
56     CHECK_AND_RETURN_RET_LOG(ret == AVCODEC_SAMPLE_ERR_OK, ret, "Create video codec failed");
57 
58     ret = Init();
59     CHECK_AND_RETURN_RET_LOG(ret == AVCODEC_SAMPLE_ERR_OK, ret, "Init failed");
60     PrintSampleInfo(info);
61 
62     ret = videoCodec->Config(info, reinterpret_cast<uintptr_t *>(context_.get()));
63     CHECK_AND_RETURN_RET_LOG(ret == AVCODEC_SAMPLE_ERR_OK, ret, "Video codec config failed");
64 
65     AVCODEC_LOGI("Succeed");
66     return AVCODEC_SAMPLE_ERR_OK;
67 }
68 
Start()69 int32_t VideoSampleBase::Start()
70 {
71     std::lock_guard<std::mutex> lock(mutex_);
72     CHECK_AND_RETURN_RET_LOG(context_ != nullptr, AVCODEC_SAMPLE_ERR_ERROR, "Already started.");
73 
74     auto &videoCodec = context_->videoCodec;
75     CHECK_AND_RETURN_RET_LOG(videoCodec != nullptr, AVCODEC_SAMPLE_ERR_ERROR, "Already started.");
76 
77     int32_t ret = videoCodec->Start();
78     CHECK_AND_RETURN_RET_LOG(ret == AVCODEC_SAMPLE_ERR_OK, ret, "Codec start failed");
79 
80     ret = Prepare();
81     CHECK_AND_RETURN_RET_LOG(ret == AVCODEC_SAMPLE_ERR_OK, ret, "Prepare failed");
82 
83     AVCODEC_LOGI("Succeed");
84     return AVCODEC_SAMPLE_ERR_OK;
85 }
86 
Init()87 int32_t VideoSampleBase::Init()
88 {
89     return AVCODEC_SAMPLE_ERR_OK;
90 }
91 
Prepare()92 int32_t VideoSampleBase::Prepare()
93 {
94     return AVCODEC_SAMPLE_ERR_OK;
95 }
96 
Release()97 void VideoSampleBase::Release()
98 {
99     std::lock_guard<std::mutex> lock(mutex_);
100     if (inputThread_ && inputThread_->joinable()) {
101         inputThread_->join();
102     }
103     if (outputThread_ && outputThread_->joinable()) {
104         outputThread_->join();
105     }
106     inputThread_ = nullptr;
107     outputThread_ = nullptr;
108     context_ = nullptr;
109     dataProducer_ = nullptr;
110     outputFile_ = nullptr;
111 
112     AVCODEC_LOGI("Succeed");
113 }
114 
InnerRelease()115 void VideoSampleBase::InnerRelease()
116 {
117     Release();
118 }
119 
DumpOutput(const CodecBufferInfo & bufferInfo)120 void VideoSampleBase::DumpOutput(const CodecBufferInfo &bufferInfo)
121 {
122     auto &info = *context_->sampleInfo;
123     CHECK_AND_RETURN(info.needDumpOutput);
124 
125     if (outputFile_ == nullptr) {
126         auto time = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
127         if (info.outputFilePath.empty()) {
128             if (!(info.codecType & 0b10)) {  // 0b10: Video encoder mask
129                 info.outputFilePath = "VideoDecoderOut_"s + ToString(info.pixelFormat) + "_" +
130                     std::to_string(info.videoWidth) + "_" + std::to_string(info.videoHeight) + "_" +
131                     std::to_string(time) + ".yuv";
132             } else {
133                 info.outputFilePath = "VideoEncoderOut_"s + std::to_string(time) + ".bin";
134             }
135         }
136 
137         outputFile_ = std::make_unique<std::ofstream>(info.outputFilePath, std::ios::out | std::ios::trunc);
138         if (!outputFile_->is_open()) {
139             outputFile_ = nullptr;
140             AVCODEC_LOGE("Output file open failed");
141             return;
142         }
143     }
144 
145     uint8_t *bufferAddr = nullptr;
146     if (bufferInfo.bufferAddr != nullptr) {
147         bufferAddr = bufferInfo.bufferAddr;
148     } else {
149         bufferAddr = static_cast<uint8_t>(info.codecRunMode) & 0b10 ?    // 0b10: AVBuffer mode mask
150                         OH_AVBuffer_GetAddr(reinterpret_cast<OH_AVBuffer *>(bufferInfo.buffer)) :
151                         OH_AVMemory_GetAddr(reinterpret_cast<OH_AVMemory *>(bufferInfo.buffer));
152     }
153 
154     CHECK_AND_RETURN_LOG(bufferAddr != nullptr, "Buffer is nullptr");
155     if (!(info.codecType & 0b10)) {   // 0b10: Video encoder mask
156         WriteOutputFileWithStrideYUV420(bufferAddr);
157     } else {
158         outputFile_->write(reinterpret_cast<char *>(bufferAddr), bufferInfo.attr.size);
159     }
160 }
161 
WriteOutputFileWithStrideYUV420(uint8_t * bufferAddr)162 void VideoSampleBase::WriteOutputFileWithStrideYUV420(uint8_t *bufferAddr)
163 {
164     CHECK_AND_RETURN_LOG(bufferAddr != nullptr, "Buffer is nullptr");
165     auto &info = *context_->sampleInfo;
166     constexpr int8_t yuvSampleRatio = 2;
167     int32_t videoWidth = info.videoWidth *
168         ((info.codecMime == OH_AVCODEC_MIMETYPE_VIDEO_HEVC && info.profile == HEVC_PROFILE_MAIN_10) ? 2 : 1);
169     int32_t &videoStrideWidth = info.videoStrideWidth;
170 
171     // copy Y
172     for (int32_t row = 0; row < info.videoHeight; row++) {
173         outputFile_->write(reinterpret_cast<char *>(bufferAddr), videoWidth);
174         bufferAddr += videoStrideWidth;
175     }
176     bufferAddr += (info.videoSliceHeight - info.videoHeight) * videoStrideWidth;
177 
178     // copy UV
179     for (int32_t row = 0; row < (info.videoHeight / yuvSampleRatio); row++) {
180         outputFile_->write(reinterpret_cast<char *>(bufferAddr), videoWidth);
181         bufferAddr += videoStrideWidth;
182     }
183 }
184 
PushEosFrame()185 void VideoSampleBase::PushEosFrame()
186 {
187     auto bufferInfoOpt = context_->inputBufferQueue.DequeueBuffer(100); // 100ms
188     CHECK_AND_RETURN(bufferInfoOpt != std::nullopt);
189     auto &bufferInfo = bufferInfoOpt.value();
190 
191     bufferInfo.attr.flags = AVCODEC_BUFFER_FLAGS_EOS;
192 
193     (void)context_->videoCodec->PushInput(bufferInfo);
194 }
195 } // Sample
196 } // MediaAVCodec
197 } // OHOS