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 }