• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2025 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 <dirent.h>
17 #include <filesystem>
18 #include <fstream>
19 #include <iostream>
20 #include <chrono>
21 #include <map>
22 #include <cstdio>
23 #include <cinttypes>
24 #include <securec.h>
25 #include <sys/mman.h>
26 #include <sys/stat.h>
27 #include "surface_buffer.h"
28 #include "v2_0/cm_color_space.h"
29 #include "v2_0/buffer_handle_meta_key_type.h"
30 #include "log.h"
31 #include "codec_heif_decode_helper.h"
32 
33 namespace OHOS::VDI::HEIF {
34 using namespace std;
35 using namespace OHOS::HDI::Codec::Image::V2_1;
36 using namespace OHOS::HDI::Display::Graphic::Common::V2_0;
37 
SplitString(const std::string & src,char sep,std::vector<std::string> & vec)38 void HeifDecoderHelper::InputParser::SplitString(const std::string& src, char sep, std::vector<std::string>& vec)
39 {
40     vec.clear();
41     string::size_type startPos = 0;
42     while (true) {
43         string::size_type endPos = src.find_first_of(sep, startPos);
44         if (endPos == string::npos) {
45             break;
46         }
47         vec.emplace_back(src.substr(startPos, endPos - startPos));
48         startPos = endPos + 1;
49     }
50     if (startPos != string::npos) {
51         vec.emplace_back(src.substr(startPos));
52     }
53 }
54 
JoinPath(const std::string & base,const std::string & append)55 std::string HeifDecoderHelper::InputParser::JoinPath(const std::string& base, const std::string& append)
56 {
57     return (filesystem::path(base) / append).string();
58 }
59 
ParseGridInfo(GridInfo & gridInfo)60 bool HeifDecoderHelper::InputParser::ParseGridInfo(GridInfo& gridInfo)
61 {
62     // source_ demo:
63     // 1. has grid: 3072x4096_grid_512x512_6x8
64     // 2. no grid: 3072x4096_nogrid
65     string baseDir = filesystem::path(source_).filename().string();
66     vector<string> vec;
67     SplitString(baseDir, MAIN_SEP, vec);
68     IF_TRUE_RETURN_VAL_WITH_MSG(vec.size() < MIN_MAIN_SEG_CNT, false,
69                                 "invalid source: %{public}s", source_.c_str());
70 
71     vector<string> vecTmp;
72     SplitString(vec[DISPLAY_SIZE], SUB_SEP, vecTmp);
73     IF_TRUE_RETURN_VAL_WITH_MSG(vecTmp.size() != SUB_SEG_CNT, false, "invalid source: %{public}s", source_.c_str());
74     gridInfo.displayWidth = static_cast<uint32_t>(stol(vecTmp[HORIZONTAL].c_str()));
75     gridInfo.displayHeight = static_cast<uint32_t>(stol(vecTmp[VERTICAL].c_str()));
76 
77     if (vec[GRID_FLAG].find(NO_GRID_INDICATOR) != string::npos) {
78         gridInfo.enableGrid = false;
79         gridInfo.cols = 1;
80         gridInfo.rows = 1;
81         gridInfo.tileWidth = gridInfo.displayWidth;
82         gridInfo.tileHeight = gridInfo.displayHeight;
83     } else {
84         IF_TRUE_RETURN_VAL_WITH_MSG(vec.size() < MAX_MAIN_SEG_CNT, false,
85                                     "invalid source: %{public}s", source_.c_str());
86 
87         gridInfo.enableGrid = true;
88 
89         SplitString(vec[TILE_SIZE], SUB_SEP, vecTmp);
90         IF_TRUE_RETURN_VAL_WITH_MSG(vecTmp.size() != SUB_SEG_CNT, false,
91                                     "invalid source: %{public}s", source_.c_str());
92         gridInfo.tileWidth = static_cast<uint32_t>(stol(vecTmp[HORIZONTAL].c_str()));
93         gridInfo.tileHeight = static_cast<uint32_t>(stol(vecTmp[VERTICAL].c_str()));
94 
95         SplitString(vec[GRID_SIZE], SUB_SEP, vecTmp);
96         IF_TRUE_RETURN_VAL_WITH_MSG(vecTmp.size() != SUB_SEG_CNT, false,
97                                     "invalid source: %{public}s", source_.c_str());
98         gridInfo.cols = static_cast<uint32_t>(stol(vecTmp[HORIZONTAL].c_str()));
99         gridInfo.rows = static_cast<uint32_t>(stol(vecTmp[VERTICAL].c_str()));
100     }
101     return true;
102 }
103 
FindXpsAndIFrameFile()104 void HeifDecoderHelper::InputParser::FindXpsAndIFrameFile()
105 {
106     DIR *dirp = opendir(source_.c_str());
107     IF_TRUE_RETURN_WITH_MSG(dirp == nullptr, "failed to open: %{public}s, errno=%{public}d",
108                             source_.c_str(), errno);
109     struct dirent *dp;
110     while ((dp = readdir(dirp)) != nullptr) {
111         if (strcmp(dp->d_name, ".") == 0 || strcmp(dp->d_name, "..") == 0) {
112             continue;
113         }
114         string path = JoinPath(source_, dp->d_name);
115         struct stat st{};
116         if (stat(path.c_str(), &st) != 0 || !S_ISREG(st.st_mode)) {
117             continue;
118         }
119         string fileName(dp->d_name);
120         if (fileName.find(XPS_INDICATOR) != string::npos) {
121             xpsFile_ = path;
122         } else if (fileName.find(I_FRAME_INDICATOR) != string::npos) {
123             iFrameFile_.emplace_back(path);
124         }
125     }
126     closedir(dirp);
127 }
128 
ReadFileToAshmem(const string & filePath,vector<sptr<Ashmem>> & inputs)129 bool HeifDecoderHelper::InputParser::ReadFileToAshmem(const string& filePath, vector<sptr<Ashmem>>& inputs)
130 {
131     ifstream ifs(filePath, ios::binary);
132     IF_TRUE_RETURN_VAL_WITH_MSG(!ifs.is_open(), false, "failed to open file: %{public}s", filePath.c_str());
133 
134     ifs.seekg(0, ifstream::end);
135     size_t fileSize = static_cast<size_t>(ifs.tellg());
136     ifs.seekg(0, ifstream::beg);
137 
138     sptr<Ashmem> ashmem = Ashmem::CreateAshmem(filePath.c_str(), static_cast<int32_t>(fileSize));
139     IF_TRUE_RETURN_VAL_WITH_MSG(ashmem == nullptr, false, "failed to create ashmem for %{public}s, size(%{public}zu)",
140                                 filePath.c_str(), fileSize);
141     int fd = ashmem->GetAshmemFd();
142     void* addr = ::mmap(nullptr, static_cast<int32_t>(fileSize), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
143     IF_TRUE_RETURN_VAL_WITH_MSG(addr == nullptr, false, "failed to map ashmem for %{public}s, size(%{public}zu)",
144                                 filePath.c_str(), fileSize);
145     ifs.read(reinterpret_cast<char*>(addr), static_cast<streamsize>(fileSize));
146     ifs.close();
147     ::munmap(addr, static_cast<int32_t>(fileSize));
148     inputs.emplace_back(ashmem);
149     return true;
150 }
151 
ExtractIFrameNum(const string & filePath)152 int HeifDecoderHelper::InputParser::ExtractIFrameNum(const string& filePath)
153 {
154     string fileName = filesystem::path(filePath).filename().string();
155     string::size_type pos = fileName.find(I_FRAME_INDICATOR);
156     if (pos == string::npos) {
157         return -1;
158     }
159     return stoi(fileName.substr(pos + string(I_FRAME_INDICATOR).size()));
160 }
161 
ReadInput(vector<sptr<Ashmem>> & inputs)162 bool HeifDecoderHelper::InputParser::ReadInput(vector<sptr<Ashmem>>& inputs)
163 {
164     FindXpsAndIFrameFile();
165     IF_TRUE_RETURN_VAL_WITH_MSG(xpsFile_.empty(), false, "no xps file in %{public}s", source_.c_str());
166     IF_TRUE_RETURN_VAL_WITH_MSG(iFrameFile_.empty(), false, "no iframe file in %{public}s", source_.c_str());
167 
168     IF_TRUE_RETURN_VAL_WITH_MSG(!ReadFileToAshmem(xpsFile_, inputs), false,
169                                 "failed to read xps file: %{public}s", xpsFile_.c_str());
170     std::sort(iFrameFile_.begin(), iFrameFile_.end(), [](const string& a, const string& b) {
171         return ExtractIFrameNum(a) < ExtractIFrameNum(b);
172     });
173     for (const string& one : iFrameFile_) {
174         IF_TRUE_RETURN_VAL_WITH_MSG(!ReadFileToAshmem(one, inputs), false,
175                                     "failed to read iframe file: %{public}s", one.c_str());
176     }
177     return true;
178 }
179 
IsValueInRange(uint32_t value,uint32_t maxValue,uint32_t minValue)180 static bool IsValueInRange(uint32_t value, uint32_t maxValue, uint32_t minValue)
181 {
182     return (value >= minValue) && (value <= maxValue);
183 }
184 
IsHeifHardwareDecodeSupported(sptr<ICodecImage> & hdiHeifDecoder)185 bool HeifDecoderHelper::IsHeifHardwareDecodeSupported(sptr<ICodecImage>& hdiHeifDecoder)
186 {
187     std::vector<CodecImageCapability> capList;
188     auto ret = hdiHeifDecoder->GetImageCapability(capList);
189     IF_TRUE_RETURN_VAL(ret != HDF_SUCCESS, false);
190     for (const CodecImageCapability& one : capList) {
191         if (one.role != CODEC_IMAGE_HEIF || one.type != CODEC_IMAGE_TYPE_DECODER) {
192             continue;
193         }
194         uint32_t widthToCheck = decInfo_.gridInfo.enableGrid ?
195                                 decInfo_.gridInfo.tileWidth :
196                                 decInfo_.gridInfo.displayWidth;
197         uint32_t heightToCheck = decInfo_.gridInfo.enableGrid ?
198                                  decInfo_.gridInfo.tileHeight :
199                                  decInfo_.gridInfo.displayHeight;
200         if (IsValueInRange(widthToCheck, one.maxWidth, one.minWidth) &&
201             IsValueInRange(heightToCheck, one.maxHeight, one.minHeight)) {
202             return true;
203         }
204         if (IsValueInRange(widthToCheck, one.maxHeight, one.minHeight) &&
205             IsValueInRange(heightToCheck, one.maxWidth, one.minWidth)) {
206             return true;
207         }
208     }
209     return false;
210 }
211 
DoDecode()212 void HeifDecoderHelper::DoDecode()
213 {
214     cout << "start heif decode" << endl;
215     IF_TRUE_RETURN_WITH_MSG(!GetOutputFormat(), "failed to get output format");
216     std::vector<sptr<Ashmem>> inputs;
217     IF_TRUE_RETURN_WITH_MSG(!ReadInput(inputs), "failed to read input");
218     GetSampleSize();
219     sptr<NativeBuffer> output;
220     IF_TRUE_RETURN_WITH_MSG(!AllocateOutputBuffer(output), "failed to allocate output buffer");
221     sptr<ICodecImage> hdiHeifDecoder = ICodecImage::Get();
222     IF_TRUE_RETURN_WITH_MSG(hdiHeifDecoder == nullptr, "failed to get ICodecImage");
223     IF_TRUE_RETURN_WITH_MSG(!IsHeifHardwareDecodeSupported(hdiHeifDecoder), "heif hw decode not supported");
224     int32_t ret = hdiHeifDecoder->DoHeifDecode(inputs, output, decInfo_);
225     if (ret == HDF_SUCCESS) {
226         cout << "heif decode succeed" << endl;
227         DumpOutput(output);
228     } else {
229         cout << "heif decode failed" << endl;
230     }
231 }
232 
GetOutputFormat()233 bool HeifDecoderHelper::GetOutputFormat()
234 {
235     static const map<UserPixelFormat,   OHOS::HDI::Display::Composer::V1_2::PixelFormat> pixelFmtMap = {
236         { UserPixelFormat::NV12,        OHOS::HDI::Display::Composer::V1_2::PIXEL_FMT_YCBCR_420_SP },
237         { UserPixelFormat::NV21,        OHOS::HDI::Display::Composer::V1_2::PIXEL_FMT_YCRCB_420_SP },
238         { UserPixelFormat::NV12_10BIT,  OHOS::HDI::Display::Composer::V1_2::PIXEL_FMT_YCBCR_P010 },
239         { UserPixelFormat::NV21_10BIT,  OHOS::HDI::Display::Composer::V1_2::PIXEL_FMT_YCRCB_P010 },
240         { UserPixelFormat::RGBA8888,    OHOS::HDI::Display::Composer::V1_2::PIXEL_FMT_RGBA_8888 },
241         { UserPixelFormat::BGRA8888,    OHOS::HDI::Display::Composer::V1_2::PIXEL_FMT_BGRA_8888 },
242         { UserPixelFormat::RGB565,      OHOS::HDI::Display::Composer::V1_2::PIXEL_FMT_RGB_565 },
243         { UserPixelFormat::RGBA1010102, OHOS::HDI::Display::Composer::V1_2::PIXEL_FMT_RGBA_1010102 },
244     };
245     auto iterFmt = pixelFmtMap.find(decodeOpt_.pixelFmt);
246     IF_TRUE_RETURN_VAL_WITH_MSG(iterFmt == pixelFmtMap.end(), false,
247                                 "unsupported pixel format: %{public}d", static_cast<int>(decodeOpt_.pixelFmt));
248     outputFormat_.format = iterFmt->second;
249 
250     static const map<OHOS::HDI::Display::Composer::V1_2::PixelFormat, string> pixelFmtDescMap = {
251         { OHOS::HDI::Display::Composer::V1_2::PIXEL_FMT_YCBCR_420_SP, "NV12"       },
252         { OHOS::HDI::Display::Composer::V1_2::PIXEL_FMT_YCRCB_420_SP, "NV21"       },
253         { OHOS::HDI::Display::Composer::V1_2::PIXEL_FMT_YCBCR_P010,   "NV12_10BIT" },
254         { OHOS::HDI::Display::Composer::V1_2::PIXEL_FMT_YCRCB_P010,   "NV21_10BIT" },
255         { OHOS::HDI::Display::Composer::V1_2::PIXEL_FMT_RGBA_8888,    "RGBA8888"   },
256         { OHOS::HDI::Display::Composer::V1_2::PIXEL_FMT_BGRA_8888,    "BGRA8888"   },
257         { OHOS::HDI::Display::Composer::V1_2::PIXEL_FMT_RGB_565,      "RGB565"     },
258         { OHOS::HDI::Display::Composer::V1_2::PIXEL_FMT_RGBA_1010102, "RGBA1010102"},
259     };
260     auto iterDesc = pixelFmtDescMap.find(outputFormat_.format);
261     IF_TRUE_RETURN_VAL_WITH_MSG(iterDesc == pixelFmtDescMap.end(), false,
262                                 "unsupported pixel format: %{public}d", static_cast<int>(decodeOpt_.pixelFmt));
263     outputFormat_.desc = iterDesc->second;
264     return true;
265 }
266 
ReadInput(vector<sptr<Ashmem>> & inputs)267 bool HeifDecoderHelper::ReadInput(vector<sptr<Ashmem>>& inputs)
268 {
269     InputParser parser(decodeOpt_.inputPath);
270     bool ret = parser.ParseGridInfo(decInfo_.gridInfo);
271     ret = ret && parser.ReadInput(inputs);
272     return ret;
273 }
274 
GetMetaDataInfo(CM_ColorSpaceInfo & colorSpaceInfo)275 void HeifDecoderHelper::GetMetaDataInfo(CM_ColorSpaceInfo& colorSpaceInfo)
276 {
277     colorSpaceInfo.range = (decodeOpt_.isLimitedRange)? RANGE_LIMITED : RANGE_FULL;
278     static const map<ColorSpace, CM_Matrix> colorSpaceDescMap = {
279         { ColorSpace::BT_601_P, MATRIX_BT601_P },
280         { ColorSpace::BT_601_N, MATRIX_BT601_N },
281         { ColorSpace::P3,       MATRIX_P3      },
282         { ColorSpace::BT_709,   MATRIX_BT709   },
283         { ColorSpace::BT_2020,  MATRIX_BT2020  },
284     };
285 
286     auto iterDesc = colorSpaceDescMap.find(decodeOpt_.colorSpace);
287     if (iterDesc == colorSpaceDescMap.end()) {
288         HDF_LOGE("unsupported colorSpace: %{public}d",  static_cast<uint32_t>(decodeOpt_.colorSpace));
289         colorSpaceInfo.matrix = MATRIX_BT601_P;
290         return;
291     }
292 
293     colorSpaceInfo.matrix = iterDesc->second;
294     return;
295 }
296 
AllocateOutputBuffer(sptr<NativeBuffer> & output)297 bool HeifDecoderHelper::AllocateOutputBuffer(sptr<NativeBuffer>& output)
298 {
299     uint64_t usage = BUFFER_USAGE_CPU_READ |
300                      BUFFER_USAGE_CPU_WRITE |
301                      BUFFER_USAGE_MEM_DMA |
302                      BUFFER_USAGE_MEM_MMZ_CACHE;
303 
304     sptr<OHOS::SurfaceBuffer> surfaceBuffer = SurfaceBuffer::Create();
305     IF_TRUE_RETURN_VAL_WITH_MSG(surfaceBuffer == nullptr, false, "failed to create buffer\n");
306 
307     BufferRequestConfig config = {
308         .width = decInfo_.gridInfo.displayWidth / decInfo_.sampleSize,
309         .height = decInfo_.gridInfo.displayHeight / decInfo_.sampleSize,
310         .strideAlignment = 32,
311         .format = outputFormat_.format,
312         .usage = usage,
313         .timeout = 0
314     };
315 
316     GSError ret = surfaceBuffer->Alloc(config);
317     IF_TRUE_RETURN_VAL_WITH_MSG(ret != GSERROR_OK, false, "failed to alloc surfaceBuffer, ret=%{public}d\n", ret);
318 
319     CM_ColorSpaceInfo colorSpaceInfo = {};
320     GetMetaDataInfo(colorSpaceInfo);
321     surfaceBuffer->SetMetadata(ATTRKEY_COLORSPACE_INFO, Pod2Vec(colorSpaceInfo));
322 
323     output = new NativeBuffer(surfaceBuffer->GetBufferHandle());
324 
325     return true;
326 }
327 
GetTimestampInMs()328 static int64_t GetTimestampInMs()
329 {
330     auto now = chrono::steady_clock::now();
331     return chrono::duration_cast<chrono::milliseconds>(now.time_since_epoch()).count();
332 }
333 
DumpOutput(sptr<NativeBuffer> & output)334 void HeifDecoderHelper::DumpOutput(sptr<NativeBuffer>& output)
335 {
336     cout << "dump heif decode result" << endl;
337     sptr<SurfaceBuffer> outputSurface = SurfaceBuffer::Create();
338     IF_TRUE_RETURN_WITH_MSG(outputSurface == nullptr, "output is null");
339     outputSurface->SetBufferHandle(output->Move());
340     int64_t timestamp = GetTimestampInMs();
341     char outputFilePath[MAX_PATH_LEN] = {0};
342     int ret = 0;
343     if (decInfo_.gridInfo.enableGrid) {
344         ret = sprintf_s(outputFilePath, sizeof(outputFilePath),
345                         "%s/%ld_hdiout_%s_%u(%d)x%u(%d)_grid_%ux%u_%ux%u_s%u_m%d_r%d.bin",
346                         DUMP_PATH, timestamp, outputFormat_.desc.c_str(),
347                         decInfo_.gridInfo.displayWidth, outputSurface->GetStride(),
348                         decInfo_.gridInfo.displayHeight, outputSurface->GetHeight(),
349                         decInfo_.gridInfo.tileWidth, decInfo_.gridInfo.tileHeight,
350                         decInfo_.gridInfo.cols, decInfo_.gridInfo.rows, decInfo_.sampleSize,
351                         static_cast<int32_t>(decodeOpt_.colorSpace), decodeOpt_.isLimitedRange);
352     } else {
353         ret = sprintf_s(outputFilePath, sizeof(outputFilePath),
354                         "%s/%ld_hdiout_%s_%u(%d)x%u(%d)_nogrid_s%u_m%d_r%d.bin",
355                         DUMP_PATH, timestamp, outputFormat_.desc.c_str(),
356                         decInfo_.gridInfo.displayWidth, outputSurface->GetStride(),
357                         decInfo_.gridInfo.displayHeight, outputSurface->GetHeight(),
358                         decInfo_.sampleSize, static_cast<int32_t>(decodeOpt_.colorSpace),
359                         decodeOpt_.isLimitedRange);
360     }
361     if (ret == -1) {
362         HDF_LOGE("failed to create dump file");
363         return;
364     }
365     cout << "dump result to: " << outputFilePath << endl;
366 
367     std::ofstream dumpOutFile;
368     dumpOutFile.open(std::string(outputFilePath), std::ios_base::binary | std::ios_base::trunc);
369     if (!dumpOutFile.is_open()) {
370         cout << "failed to dump decode result" << endl;
371         return;
372     }
373 
374     GSError err = outputSurface->InvalidateCache();
375     if (err != GSERROR_OK) {
376         cout << "InvalidateCache failed, GSError=" << err << endl;
377     }
378     dumpOutFile.write(reinterpret_cast<char*>(outputSurface->GetVirAddr()), outputSurface->GetSize());
379     dumpOutFile.close();
380 }
381 
GetSampleSize()382 void HeifDecoderHelper::GetSampleSize()
383 {
384     static const map<SampleSize, uint32_t> sampleSizeMap = {
385         { SampleSize::SAMPLE_SIZE_1,  1  },
386         { SampleSize::SAMPLE_SIZE_2,  2  },
387         { SampleSize::SAMPLE_SIZE_4,  4  },
388         { SampleSize::SAMPLE_SIZE_8,  8  },
389         { SampleSize::SAMPLE_SIZE_16, 16 },
390     };
391     auto iter = sampleSizeMap.find(decodeOpt_.sampleSize);
392     if (iter != sampleSizeMap.end()) {
393         decInfo_.sampleSize = iter->second;
394     } else {
395         decInfo_.sampleSize = 1;
396     }
397 }
398 }