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