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 "mock_heif_hw_decode_flow.h"
17 #include "hardware/imagecodec/image_codec_log.h"
18 #include "media_errors.h"
19 #include <map>
20 #include <fstream>
21 #include <filesystem>
22 #include <dirent.h>
23 #include <sys/stat.h>
24 #include <algorithm>
25 
26 namespace OHOS::ImagePlugin {
27 using namespace std;
28 
SplitString(const std::string & src,char sep,std::vector<std::string> & vec)29 void HeifHwDecoderFlow::InputParser::SplitString(const std::string& src, char sep, std::vector<std::string>& vec)
30 {
31     vec.clear();
32     string::size_type startPos = 0;
33     while (true) {
34         string::size_type endPos = src.find_first_of(sep, startPos);
35         if (endPos == string::npos) {
36             break;
37         }
38         vec.emplace_back(src.substr(startPos, endPos - startPos));
39         startPos = endPos + 1;
40     }
41     if (startPos != string::npos) {
42         vec.emplace_back(src.substr(startPos));
43     }
44 }
45 
JoinPath(const std::string & base,const std::string & append)46 std::string HeifHwDecoderFlow::InputParser::JoinPath(const std::string& base, const std::string& append)
47 {
48     return (filesystem::path(base) / append).string();
49 }
50 
ParseGridInfo(GridInfo & gridInfo)51 bool HeifHwDecoderFlow::InputParser::ParseGridInfo(GridInfo& gridInfo)
52 {
53     // source_ demo:
54     // 1. has grid: 3072x4096_grid_512x512_6x8
55     // 2. no grid: 3072x4096_nogrid
56     string baseDir = filesystem::path(source_).filename().string();
57     vector<string> vec;
58     SplitString(baseDir, MAIN_SEP, vec);
59     IF_TRUE_RETURN_VAL_WITH_MSG(vec.size() < MIN_MAIN_SEG_CNT, false,
60                                 "invalid source: %{public}s", source_.c_str());
61 
62     vector<string> vecTmp;
63     SplitString(vec[DISPLAY_SIZE], SUB_SEP, vecTmp);
64     IF_TRUE_RETURN_VAL_WITH_MSG(vecTmp.size() != SUB_SEG_CNT, false, "invalid source: %{public}s", source_.c_str());
65     gridInfo.displayWidth = static_cast<uint32_t>(stol(vecTmp[HORIZONTAL].c_str()));
66     gridInfo.displayHeight = static_cast<uint32_t>(stol(vecTmp[VERTICAL].c_str()));
67 
68     if (vec[GRID_FLAG].find(NO_GRID_INDICATOR) != string::npos) {
69         gridInfo.enableGrid = false;
70         gridInfo.cols = 0;
71         gridInfo.rows = 0;
72         gridInfo.tileWidth = 0;
73         gridInfo.tileHeight = 0;
74     } else {
75         IF_TRUE_RETURN_VAL_WITH_MSG(vec.size() < MAX_MAIN_SEG_CNT, false,
76                                     "invalid source: %{public}s", source_.c_str());
77 
78         gridInfo.enableGrid = true;
79 
80         SplitString(vec[TILE_SIZE], SUB_SEP, vecTmp);
81         IF_TRUE_RETURN_VAL_WITH_MSG(vecTmp.size() != SUB_SEG_CNT, false,
82                                     "invalid source: %{public}s", source_.c_str());
83         gridInfo.tileWidth = static_cast<uint32_t>(stol(vecTmp[HORIZONTAL].c_str()));
84         gridInfo.tileHeight = static_cast<uint32_t>(stol(vecTmp[VERTICAL].c_str()));
85 
86         SplitString(vec[GRID_SIZE], SUB_SEP, vecTmp);
87         IF_TRUE_RETURN_VAL_WITH_MSG(vecTmp.size() != SUB_SEG_CNT, false,
88                                     "invalid source: %{public}s", source_.c_str());
89         gridInfo.cols = static_cast<uint32_t>(stol(vecTmp[HORIZONTAL].c_str()));
90         gridInfo.rows = static_cast<uint32_t>(stol(vecTmp[VERTICAL].c_str()));
91     }
92     return true;
93 }
94 
FindXpsAndIFrameFile()95 void HeifHwDecoderFlow::InputParser::FindXpsAndIFrameFile()
96 {
97     DIR *dirp = opendir(source_.c_str());
98     IF_TRUE_RETURN_VOID_WITH_MSG(dirp == nullptr, "failed to open: %{public}s, errno=%{public}d",
99                                  source_.c_str(), errno);
100     struct dirent *dp;
101     while ((dp = readdir(dirp)) != nullptr) {
102         if (strcmp(dp->d_name, ".") == 0 || strcmp(dp->d_name, "..") == 0) {
103             continue;
104         }
105         string path = JoinPath(source_, dp->d_name);
106         struct stat st{};
107         if (stat(path.c_str(), &st) != 0 || !S_ISREG(st.st_mode)) {
108             continue;
109         }
110         string fileName(dp->d_name);
111         if (fileName.find(XPS_INDICATOR) != string::npos) {
112             xpsFile_ = path;
113         } else if (fileName.find(I_FRAME_INDICATOR) != string::npos) {
114             iFrameFile_.emplace_back(path);
115         }
116     }
117     closedir(dirp);
118 }
119 
ReadFileToVec(const string & filePath,vector<vector<uint8_t>> & inputs)120 bool HeifHwDecoderFlow::InputParser::ReadFileToVec(const string& filePath, vector<vector<uint8_t>>& inputs)
121 {
122     ifstream ifs(filePath, ios::binary);
123     IF_TRUE_RETURN_VAL_WITH_MSG(!ifs.is_open(), false, "failed to open file: %{public}s", filePath.c_str());
124 
125     ifs.seekg(0, ifstream::end);
126     size_t fileSize = static_cast<size_t>(ifs.tellg());
127     ifs.seekg(0, ifstream::beg);
128 
129     vector<uint8_t> vec(fileSize);
130     ifs.read(reinterpret_cast<char*>(vec.data()), static_cast<streamsize>(fileSize));
131     ifs.close();
132     inputs.emplace_back(vec);
133     return true;
134 }
135 
ExtractIFrameNum(const string & filePath)136 int HeifHwDecoderFlow::InputParser::ExtractIFrameNum(const string& filePath)
137 {
138     string fileName = filesystem::path(filePath).filename().string();
139     string::size_type pos = fileName.find(I_FRAME_INDICATOR);
140     if (pos == string::npos) {
141         return -1;
142     }
143     return stoi(fileName.substr(pos + string(I_FRAME_INDICATOR).size()));
144 }
145 
ReadInput(vector<vector<uint8_t>> & inputs)146 bool HeifHwDecoderFlow::InputParser::ReadInput(vector<vector<uint8_t>>& inputs)
147 {
148     FindXpsAndIFrameFile();
149     IF_TRUE_RETURN_VAL_WITH_MSG(xpsFile_.empty(), false, "no xps file in %{public}s", source_.c_str());
150     IF_TRUE_RETURN_VAL_WITH_MSG(iFrameFile_.empty(), false, "no iframe file in %{public}s", source_.c_str());
151 
152     IF_TRUE_RETURN_VAL_WITH_MSG(!ReadFileToVec(xpsFile_, inputs), false,
153                                 "failed to read xps file: %{public}s", xpsFile_.c_str());
154     std::sort(iFrameFile_.begin(), iFrameFile_.end(), [](const string& a, const string& b) {
155         return ExtractIFrameNum(a) < ExtractIFrameNum(b);
156     });
157     for (const string& one : iFrameFile_) {
158         IF_TRUE_RETURN_VAL_WITH_MSG(!ReadFileToVec(one, inputs), false,
159                                     "failed to read iframe file: %{public}s", one.c_str());
160     }
161     return true;
162 }
163 
~HeifHwDecoderFlow()164 HeifHwDecoderFlow::~HeifHwDecoderFlow()
165 {
166     output_ = nullptr;
167 }
168 
Run(const CommandOpt & opt)169 bool HeifHwDecoderFlow::Run(const CommandOpt& opt)
170 {
171     bool ret = PrepareInput(opt.inputPath);
172     ret = ret && AllocOutput(opt.pixelFormat);
173     ret = ret && DoDecode();
174     if (ret) {
175         LOGI("demo succeed");
176     } else {
177         LOGE("demo failed");
178     }
179     return ret;
180 }
181 
PrepareInput(const std::string & inputPath)182 bool HeifHwDecoderFlow::PrepareInput(const std::string& inputPath)
183 {
184     InputParser parser(inputPath);
185     bool ret = parser.ParseGridInfo(gridInfo_);
186     ret = ret && parser.ReadInput(input_);
187     return ret;
188 }
189 
AllocOutput(UserPixelFormat userPixelFormat)190 bool HeifHwDecoderFlow::AllocOutput(UserPixelFormat userPixelFormat)
191 {
192     static const map<UserPixelFormat, GraphicPixelFormat> userPixelFmtToGraphicPixelFmt = {
193         { UserPixelFormat::NV12,       GRAPHIC_PIXEL_FMT_YCBCR_420_SP },
194         { UserPixelFormat::NV21,       GRAPHIC_PIXEL_FMT_YCRCB_420_SP },
195         { UserPixelFormat::NV12_10bit, GRAPHIC_PIXEL_FMT_YCBCR_P010 },
196         { UserPixelFormat::NV21_10bit, GRAPHIC_PIXEL_FMT_YCRCB_P010 },
197     };
198     auto iter = userPixelFmtToGraphicPixelFmt.find(userPixelFormat);
199     if (iter == userPixelFmtToGraphicPixelFmt.end()) {
200         LOGE("unsupported pixel format: %{public}d", static_cast<int>(userPixelFormat));
201         return false;
202     }
203     GraphicPixelFormat pixelFmt = iter->second;
204     output_ = hwDecoder_.AllocateOutputBuffer(gridInfo_.displayWidth, gridInfo_.displayHeight, pixelFmt);
205     if (output_ == nullptr) {
206         LOGE("failed to alloc output");
207         return false;
208     }
209     return true;
210 }
211 
DoDecode()212 bool HeifHwDecoderFlow::DoDecode()
213 {
214     uint32_t ret = hwDecoder_.DoDecode(gridInfo_, input_, output_);
215     if (ret != Media::SUCCESS) {
216         LOGE("failed to decode");
217         return false;
218     }
219     return true;
220 }
221 } // namespace OHOS::ImagePlugin