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 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 <sys/mman.h>
17 #include "ashmem.h"
18 #include "buffer_helper.h"
19
20 namespace OHOS::VDI::HEIF {
21 using namespace OHOS::HDI::Codec::Image::V2_0;
22 using namespace OHOS::HDI::Display::Buffer::V1_2;
23 using namespace OHOS::HDI::Display::Composer::V1_2;
24 using namespace std;
25
GetFileSizeInBytes(ifstream & ifs)26 static size_t GetFileSizeInBytes(ifstream &ifs)
27 {
28 ifs.seekg(0, ifstream::end);
29 auto len = ifs.tellg();
30 ifs.seekg(0, ifstream::beg);
31 return static_cast<size_t>(len);
32 }
33
BufferHelper()34 BufferHelper::BufferHelper()
35 {
36 bufferMgr_ = OHOS::HDI::Display::Buffer::V1_2::IDisplayBuffer::Get();
37 }
38
~BufferHelper()39 BufferHelper::~BufferHelper()
40 {
41 bufferMgr_ = nullptr;
42 for (auto iter = allocatedFd_.begin(); iter != allocatedFd_.end(); ++iter) {
43 close(*iter);
44 }
45 allocatedFd_.clear();
46 }
47
ExtractPixelInfoFromFilePath(const string & filePath,PixelFileInfo & pixelInfo)48 bool BufferHelper::ExtractPixelInfoFromFilePath(const string& filePath, PixelFileInfo& pixelInfo)
49 {
50 size_t pos = filePath.find_last_of('/');
51 IF_TRUE_RETURN_VAL(pos == string::npos, false);
52 pos = filePath.find_first_of('[', pos);
53 IF_TRUE_RETURN_VAL(pos == string::npos, false);
54 int ret = sscanf_s(filePath.substr(pos).c_str(), "[%ux%u][%ux%u][fmt0x%x].yuv",
55 &pixelInfo.displayWidth, &pixelInfo.displayHeight,
56 &pixelInfo.alignedWidth, &pixelInfo.alignedHeight,
57 &pixelInfo.pixFmt);
58 static constexpr int EXP_CNT = 5;
59 IF_TRUE_RETURN_VAL(ret != EXP_CNT, false);
60 HDF_LOGI("pixel info: display=[%{public}u x %{public}u], aligned=[%{public}u x %{public}u]",
61 pixelInfo.displayWidth, pixelInfo.displayHeight, pixelInfo.alignedWidth, pixelInfo.alignedHeight);
62 return true;
63 }
64
CopyYuvData(BufferHandle * handle,ifstream & ifs,PixelFileInfo & pixelInfo)65 bool BufferHelper::CopyYuvData(BufferHandle *handle, ifstream &ifs, PixelFileInfo& pixelInfo)
66 {
67 static constexpr uint32_t BYTES_PER_PIXEL_YUV = 1;
68 // Y plane
69 char* dst = reinterpret_cast<char*>(handle->virAddr);
70 for (uint32_t i = 0; i < pixelInfo.displayHeight; i++) {
71 ifs.read(dst, pixelInfo.alignedWidth * BYTES_PER_PIXEL_YUV);
72 dst += handle->stride;
73 }
74 // skip aligned lines
75 for (uint32_t i = 0; i < (pixelInfo.alignedHeight - pixelInfo.displayHeight); i++) {
76 ifs.read(dst, pixelInfo.alignedWidth * BYTES_PER_PIXEL_YUV);
77 }
78 // UV plane
79 ImageLayout layout;
80 int32_t ret = bufferMgr_->GetImageLayout(*handle, layout);
81 IF_TRUE_RETURN_VAL_WITH_MSG(ret != HDF_SUCCESS, false,
82 "failed to get uv start, err [%{public}d] !", ret);
83 static constexpr int PLANE_U = 1;
84 static constexpr int PLANE_V = 2;
85 static constexpr uint32_t UV_SAMPLE_RATE = 2;
86 uint64_t uvOffset = (pixelInfo.pixFmt == OHOS::HDI::Display::Composer::V1_2::PIXEL_FMT_YCBCR_420_SP) ?
87 layout.planes[PLANE_U].offset :
88 layout.planes[PLANE_V].offset;
89 dst = reinterpret_cast<char*>(handle->virAddr) + uvOffset;
90 for (uint32_t i = 0; i < pixelInfo.displayHeight / UV_SAMPLE_RATE; i++) {
91 ifs.read(dst, pixelInfo.alignedWidth * BYTES_PER_PIXEL_YUV);
92 dst += handle->stride;
93 }
94 return true;
95 }
96
CopyRgbaData(BufferHandle * handle,ifstream & ifs,PixelFileInfo & pixelInfo)97 bool BufferHelper::CopyRgbaData(BufferHandle *handle, ifstream &ifs, PixelFileInfo& pixelInfo)
98 {
99 static constexpr uint32_t BYTES_PER_PIXEL_RBGA = 4;
100 char* dst = reinterpret_cast<char*>(handle->virAddr);
101 for (uint32_t i = 0; i < pixelInfo.displayHeight; i++) {
102 ifs.read(dst, pixelInfo.alignedWidth * BYTES_PER_PIXEL_RBGA);
103 dst += handle->stride;
104 }
105 return true;
106 }
107
GetPixelFmtFromFileSuffix(const string & imageFile)108 uint32_t BufferHelper::GetPixelFmtFromFileSuffix(const string& imageFile)
109 {
110 if (imageFile.rfind(".rgba") != string::npos) {
111 return OHOS::HDI::Display::Composer::V1_2::PIXEL_FMT_RGBA_8888;
112 }
113 if (imageFile.rfind(".nv21") != string::npos) {
114 return OHOS::HDI::Display::Composer::V1_2::PIXEL_FMT_YCRCB_420_SP;
115 }
116 return OHOS::HDI::Display::Composer::V1_2::PIXEL_FMT_YCBCR_420_SP;
117 }
118
CreateImgBuffer(const string & imageFile)119 sptr<NativeBuffer> BufferHelper::CreateImgBuffer(const string& imageFile)
120 {
121 IF_TRUE_RETURN_VAL(imageFile.length() <= 0, nullptr);
122 ifstream ifs(imageFile, ios::binary);
123 IF_TRUE_RETURN_VAL_WITH_MSG(!ifs.is_open(), nullptr, "cannot open %{public}s", imageFile.c_str());
124 PixelFileInfo pixelInfo;
125 IF_TRUE_RETURN_VAL_WITH_MSG(!ExtractPixelInfoFromFilePath(imageFile, pixelInfo), nullptr,
126 "invalid file path format: %{public}s", imageFile.c_str());
127 uint64_t usage = OHOS::HDI::Display::Composer::V1_2::HBM_USE_CPU_READ |
128 OHOS::HDI::Display::Composer::V1_2::HBM_USE_CPU_WRITE |
129 OHOS::HDI::Display::Composer::V1_2::HBM_USE_MEM_DMA;
130 pixelInfo.pixFmt = GetPixelFmtFromFileSuffix(imageFile);
131 HDF_LOGI("pixelFmt=0x%{public}x", pixelInfo.pixFmt);
132 AllocInfo alloc = {
133 .width = pixelInfo.displayWidth,
134 .height = pixelInfo.displayHeight,
135 .usage = usage,
136 .format = pixelInfo.pixFmt
137 };
138 BufferHandle *handle = nullptr;
139 int32_t ret = bufferMgr_->AllocMem(alloc, handle);
140 IF_TRUE_RETURN_VAL_WITH_MSG(ret != HDF_SUCCESS, nullptr,
141 "failed to alloc buffer, err [%{public}d] !", ret);
142 bufferMgr_->Mmap(*handle);
143 bool flag = (pixelInfo.pixFmt == OHOS::HDI::Display::Composer::V1_2::PIXEL_FMT_RGBA_8888) ?
144 CopyRgbaData(handle, ifs, pixelInfo) :
145 CopyYuvData(handle, ifs, pixelInfo);
146 (void)bufferMgr_->Unmap(*handle);
147 if (!flag) {
148 bufferMgr_->FreeMem(*handle);
149 return nullptr;
150 }
151 sptr<NativeBuffer> imgBuffer = new NativeBuffer(handle);
152 return imgBuffer;
153 }
154
CreateSharedBuffer(map<PropertyType,string> & metaInfo)155 SharedBuffer BufferHelper::CreateSharedBuffer(map<PropertyType, string>& metaInfo)
156 {
157 SharedBuffer buffer = {
158 .fd = -1,
159 .filledLen = 0,
160 .capacity = 0
161 };
162 ByteWriter bw;
163 bool flag = true;
164 for (auto iter = metaInfo.begin(); (iter != metaInfo.end()) && flag; ++iter) {
165 flag = bw.AddDataFromFile(iter->first, iter->second);
166 }
167 if (flag && bw.Finalize(buffer)) {
168 allocatedFd_.insert(buffer.fd);
169 }
170 return buffer;
171 }
172
CreateSharedBuffer(const string & metaFile)173 SharedBuffer BufferHelper::CreateSharedBuffer(const string& metaFile)
174 {
175 SharedBuffer buffer = {
176 .fd = -1,
177 .filledLen = 0,
178 .capacity = 0
179 };
180 IF_TRUE_RETURN_VAL_WITH_MSG(metaFile.length() <= 0, buffer, "no metaFile");
181 ifstream ifs(metaFile, ios::binary);
182 IF_TRUE_RETURN_VAL_WITH_MSG(!ifs.is_open(), buffer, "cannot open %{public}s", metaFile.c_str());
183 size_t totalSize = GetFileSizeInBytes(ifs);
184 int fd = AshmemCreate("ForMetaData", totalSize);
185 IF_TRUE_RETURN_VAL_WITH_MSG(fd < 0, buffer, "cannot create ashmem for meta data");
186 void *addr = mmap(nullptr, totalSize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
187 if (addr == nullptr) {
188 HDF_LOGE("failed to map addr for meta buffer");
189 close(fd);
190 return buffer;
191 }
192 ifs.read(reinterpret_cast<char*>(addr), totalSize);
193 if (munmap(addr, totalSize) != 0) {
194 HDF_LOGW("failed to unmap addr for meta buffer");
195 }
196 buffer.fd = fd;
197 buffer.filledLen = static_cast<uint32_t>(totalSize);
198 buffer.capacity = static_cast<uint32_t>(AshmemGetSize(fd));
199 allocatedFd_.insert(fd);
200 return buffer;
201 }
202
DumpBuffer(const string & filePath,const SharedBuffer & buffer)203 void BufferHelper::DumpBuffer(const string& filePath, const SharedBuffer& buffer)
204 {
205 IF_TRUE_RETURN_WITH_MSG(filePath.length() <= 0, "dump path is empty");
206 constexpr int maxPathLen = 256;
207 char outputFilePath[maxPathLen] = {0};
208 int ret = sprintf_s(outputFilePath, sizeof(outputFilePath), "%s/out.heic",
209 filePath.c_str());
210 if (ret == -1) {
211 HDF_LOGE("failed to create dump file");
212 return;
213 }
214 HDF_LOGI("dump buffer to: %{public}s", outputFilePath);
215 ofstream ofs(outputFilePath, ios::binary);
216 IF_TRUE_RETURN_WITH_MSG(!ofs.is_open(), "cannot open %{public}s", outputFilePath);
217 void *addr = mmap(nullptr, buffer.filledLen, PROT_READ | PROT_WRITE, MAP_SHARED, buffer.fd, 0);
218 if (addr != nullptr) {
219 ofs.write(static_cast<char*>(addr), static_cast<streamsize>(buffer.filledLen));
220 ofs.close();
221 } else {
222 HDF_LOGE("failed to map addr for dump buffer");
223 }
224 if (munmap(addr, buffer.filledLen) != 0) {
225 HDF_LOGW("failed to unmap addr for dump buffer");
226 }
227 }
228
~ByteWriter()229 ByteWriter::~ByteWriter()
230 {
231 for (auto iter = data_.begin(); iter != data_.end(); ++iter) {
232 delete [] iter->data;
233 }
234 data_.clear();
235 }
236
CopyDataTo(uint8_t * dstStart)237 bool ByteWriter::CopyDataTo(uint8_t* dstStart)
238 {
239 size_t offset = 0;
240 errno_t ret = EOK;
241 for (auto iter = data_.begin(); (iter != data_.end()) && (ret == EOK); ++iter) {
242 ret = memcpy_s(dstStart + offset, iter->len, iter->data, iter->len);
243 offset += iter->len;
244 }
245 return (ret == EOK);
246 }
247
Finalize(std::vector<uint8_t> & dst)248 bool ByteWriter::Finalize(std::vector<uint8_t>& dst)
249 {
250 dst.clear();
251 dst.resize(totalSize_);
252 return CopyDataTo(reinterpret_cast<uint8_t*>(dst.data()));
253 }
254
AddDataFromFile(PropertyType key,const string & filePath)255 bool ByteWriter::AddDataFromFile(PropertyType key, const string& filePath)
256 {
257 IF_TRUE_RETURN_VAL_WITH_MSG(filePath.length() <= 0, false, "no prop file");
258 ifstream ifs(filePath, ios::binary);
259 IF_TRUE_RETURN_VAL_WITH_MSG(!ifs.is_open(), false, "cannot open %{public}s", filePath.c_str());
260 size_t fileSize = GetFileSizeInBytes(ifs);
261 static constexpr size_t BYTE_TO_STORE_BUFFER_SIZE = 4;
262 std::size_t dataSize = sizeof(key) + BYTE_TO_STORE_BUFFER_SIZE + fileSize;
263 uint8_t* p = new uint8_t[dataSize];
264 IF_TRUE_RETURN_VAL(p == nullptr, false);
265 data_.emplace_back(DataBlock {
266 .data = p,
267 .len = dataSize
268 });
269 totalSize_ += dataSize;
270 errno_t ret = memset_s(p, dataSize, 0, dataSize);
271 IF_TRUE_RETURN_VAL_WITH_MSG(ret != EOK, false, "failed to init mem");
272 size_t offset = 0;
273 ret = memcpy_s(p + offset, sizeof(key), reinterpret_cast<uint8_t*>(&key), sizeof(key));
274 IF_TRUE_RETURN_VAL_WITH_MSG(ret != EOK, false, "failed to copy key");
275 offset += sizeof(key);
276 ret = memcpy_s(p + offset, BYTE_TO_STORE_BUFFER_SIZE,
277 reinterpret_cast<uint8_t*>(&fileSize), BYTE_TO_STORE_BUFFER_SIZE);
278 IF_TRUE_RETURN_VAL_WITH_MSG(ret != EOK, false, "failed to copy buffer size");
279 offset += BYTE_TO_STORE_BUFFER_SIZE;
280 ifs.read(reinterpret_cast<char*>(p) + offset, fileSize);
281 return true;
282 }
283
Finalize(SharedBuffer & buffer)284 bool ByteWriter::Finalize(SharedBuffer& buffer)
285 {
286 int fd = AshmemCreate("ForMetaProp", totalSize_);
287 IF_TRUE_RETURN_VAL_WITH_MSG(fd < 0, false, "cannot create ashmem for meta prop");
288 void *addr = mmap(nullptr, totalSize_, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
289 if (addr == nullptr) {
290 HDF_LOGE("failed to map addr for meta prop");
291 close(fd);
292 return false;
293 }
294 bool flag = CopyDataTo(reinterpret_cast<uint8_t*>(addr));
295 if (munmap(addr, totalSize_) != 0) {
296 HDF_LOGW("failed to unmap addr for meta prop");
297 }
298 if (flag) {
299 buffer.fd = fd;
300 buffer.filledLen = static_cast<uint32_t>(totalSize_);
301 buffer.capacity = static_cast<uint32_t>(AshmemGetSize(fd));
302 return true;
303 }
304 return false;
305 }
306 }