新增转码功能

Signed-off-by: zt147369 <zhangting201@huawei.com>
This commit is contained in:
zt147369
2024-06-11 18:05:46 +08:00
parent f191c7d864
commit 1602e32ecb
9 changed files with 344 additions and 27 deletions
+47 -1
View File
@@ -17,11 +17,40 @@
#define OHOS_RESTOOL_COMPRESSION_PARSER_H
#include <cJSON.h>
#ifdef __WIN32
#include <windows.h>
#else
#include <dlfcn.h>
#endif
#include "resource_util.h"
namespace OHOS {
namespace Global {
namespace Restool {
enum class TranscodeError {
SUCCESS = 0,
IMAGE_ERROR,
IMAGE_RESOLUTION_NOT_MATCH,
ANIMATED_IMAGE_SKIP,
MALLOC_FAILED,
ENCODE_ASTC_FAILED,
SUPER_COMPRESS_FAILED,
LOAD_COMPRESS_FAILED
};
struct TranscodeResult {
int64_t timeCostMs;
size_t originSize;
int32_t size;
int32_t width;
int32_t height;
};
typedef TranscodeError (*ITranscodeImages) (const std::string &imagePath,
std::string &outputPath, TranscodeResult &result);
typedef bool (*ISetTranscodeOptions) (const std::string &optionJson);
class CompressionParser {
public:
static std::shared_ptr<CompressionParser> GetCompressionParser(const std::string &filePath);
@@ -30,14 +59,31 @@ public:
explicit CompressionParser(const std::string &filePath);
virtual ~CompressionParser();
uint32_t Init();
bool CopyAndTranscode(const std::string &src, std::string &dst);
private:
bool ParseContext(const cJSON *contextNode);
bool ParseCompression(const cJSON *compressionNode);
bool ParseFilter(const cJSON *filterNode);
bool ParseFilters(const cJSON *filtersNode);
bool LoadImageTranscoder();
bool SetTranscodeOptions(const std::string &optionJson);
TranscodeError TranscodeImages(const std::string &imagePath, std::string &outputPath, TranscodeResult &result);
std::vector<std::string> ParsePath(const cJSON *pathNode);
std::vector<std::string> ParseRules(const cJSON *rulesNode);
std::string ParseJsonStr(const cJSON *node);
bool CheckPath(const std::string &src, const std::vector<std::string> &paths);
bool IsInPath(const std::string &src, const std::shared_ptr<CompressFilter> &compressFilter);
bool IsInExcludePath(const std::string &src, const std::shared_ptr<CompressFilter> &compressFilter);
std::string GetOptionsString(const std::shared_ptr<CompressFilter> &compressFilter, int type);
std::string filePath_;
std::string extensionPath_;
std::vector<std::shared_ptr<CompressFilter>> compressFilters_;
bool mediaSwitch_;
cJSON *root_;
#ifdef __WIN32
HMODULE handle_ = nullptr;
#else
void *handle_ = nullptr;
#endif
};
}
}
+2 -2
View File
@@ -27,11 +27,11 @@ public:
virtual ~GenericCompiler();
protected:
uint32_t CompileSingleFile(const FileInfo &fileInfo) override;
bool PostFile(const FileInfo &fileInfo);
bool PostMediaFile(const FileInfo &fileInfo, const std::string &output);
private:
std::string GetOutputFilePath(const FileInfo &fileInfo) const;
bool IsIgnore(const FileInfo &fileInfo) const;
bool CopyFile(const FileInfo &fileInfo) const;
bool CopyMediaFile(const FileInfo &fileInfo, std::string &output);
};
}
}
+13
View File
@@ -33,6 +33,11 @@ const static std::string RES_FILE_DIR = "resfile";
const static std::string ID_DEFINED_FILE = "id_defined.json";
const static std::string RESOURCE_INDEX_FILE = "resources.index";
const static std::string JSON_EXTENSION = ".json";
#ifdef __WIN32
const static std::string SEPARATOR_FILE = "\\";
#else
const static std::string SEPARATOR_FILE = "/";
#endif
const static std::string SEPARATOR = "/";
const static std::string WIN_SEPARATOR = "\\";
const static std::string NEW_LINE_PATH = "\nat ";
@@ -200,6 +205,14 @@ struct ResourceId {
std::string name;
};
struct CompressFilter {
std::vector<std::string> path;
std::vector<std::string> excludePath;
std::vector<std::string> rules;
std::vector<std::string> expandRules;
std::string method;
};
const std::map<std::string, ResType> g_copyFileMap = {
{ RAW_FILE_DIR, ResType::RAW },
{ RES_FILE_DIR, ResType::RES },
+1 -1
View File
@@ -122,7 +122,7 @@ public:
* @param dst: destination file path
* @return true if success, other false
*/
static bool CopyFleInner(const std::string &src, const std::string &dst);
static bool CopyFileInner(const std::string &src, const std::string &dst);
/**
* @brief create directories
+261 -12
View File
@@ -14,6 +14,8 @@
*/
#include "compression_parser.h"
#include <algorithm>
#include <iostream>
#include <mutex>
#include "restool_errors.h"
@@ -24,7 +26,22 @@ namespace Restool {
using namespace std;
static shared_ptr<CompressionParser> compressionParseMgr = nullptr;
static once_flag compressionParserMgrFlag;
constexpr int OPT_TYPE_ONE = 1;
constexpr int OPT_TYPE_TWO = 2;
constexpr int OPT_TYPE_THREE = 3;
constexpr int OPT_SIZE_ONE = 1;
constexpr int OPT_SIZE_TWO = 2;
const map<TranscodeError, string> ERRORCODEMAP = {
{ TranscodeError::SUCCESS, "SUCCESS" },
{ TranscodeError::IMAGE_RESOLUTION_NOT_MATCH, "IMAGE_RESOLUTION_NOT_MATCH" },
{ TranscodeError::ANIMATED_IMAGE_SKIP, "ANIMATED_IMAGE_SKIP" },
{ TranscodeError::MALLOC_FAILED, "MALLOC_FAILED" },
{ TranscodeError::ENCODE_ASTC_FAILED, "ENCODE_ASTC_FAILED" },
{ TranscodeError::SUPER_COMPRESS_FAILED, "SUPER_COMPRESS_FAILED" },
{ TranscodeError::LOAD_COMPRESS_FAILED, "LOAD_COMPRESS_FAILED" },
};
CompressionParser::CompressionParser()
: filePath_(""), extensionPath_(""), mediaSwitch_(false), root_(nullptr)
{
@@ -40,6 +57,17 @@ CompressionParser::~CompressionParser()
if (root_) {
cJSON_Delete(root_);
}
#ifdef __WIN32
if (handle_) {
FreeLibrary(handle_);
handle_ = nullptr;
}
#else
if (handle_) {
dlclose(handle_);
handle_ = nullptr;
}
#endif
}
shared_ptr<CompressionParser> CompressionParser::GetCompressionParser(const string &filePath)
@@ -75,11 +103,14 @@ uint32_t CompressionParser::Init()
return RESTOOL_SUCCESS;
}
cJSON *contextNode = cJSON_GetObjectItem(root_, "context");
cJSON *filterNode = cJSON_GetObjectItem(compressionNode, "filter");
if (!ParseContext(contextNode) || !ParseFilter(filterNode)) {
cJSON *filtersNode = cJSON_GetObjectItem(compressionNode, "filters");
if (!ParseContext(contextNode) || !ParseFilters(filtersNode)) {
cerr << NEW_LINE_PATH << filePath_ << endl;
return RESTOOL_ERROR;
}
if (!LoadImageTranscoder()) {
return RESTOOL_ERROR;
}
return RESTOOL_SUCCESS;
}
@@ -145,23 +176,23 @@ bool CompressionParser::ParseContext(const cJSON *contextNode)
return true;
}
bool CompressionParser::ParseFilter(const cJSON *filterNode)
bool CompressionParser::ParseFilters(const cJSON *filtersNode)
{
if (!filterNode) {
cerr << "Error: if image transcoding is supported, the 'filter' node cannot be empty.";
if (!filtersNode) {
cerr << "Error: if image transcoding is supported, the 'filters' node cannot be empty.";
return false;
}
if (!cJSON_IsArray(filterNode)) {
cerr << "Error: 'filter' must be array.";
if (!cJSON_IsArray(filtersNode)) {
cerr << "Error: 'filters' must be array.";
return false;
}
if (cJSON_GetArraySize(filterNode) == 0) {
cerr << "Error: 'filter' value cannot be empty.";
if (cJSON_GetArraySize(filtersNode) == 0) {
cerr << "Error: 'filters' value cannot be empty.";
return false;
}
for (cJSON *item = filterNode->child; item; item = item->next) {
for (cJSON *item = filtersNode->child; item; item = item->next) {
if (!cJSON_IsObject(item)) {
cerr << "Error: 'filter' value type must be object.";
cerr << "Error: 'filters' value type must be object.";
return false;
}
cJSON *methodNode = cJSON_GetObjectItem(item, "method");
@@ -173,9 +204,227 @@ bool CompressionParser::ParseFilter(const cJSON *filterNode)
cerr << "Error: 'method' must be object.";
return false;
}
shared_ptr<CompressFilter> compressFilter = make_shared<CompressFilter>();
compressFilter->method = "\"method\":" + ParseJsonStr(methodNode);
cJSON *pathNode = cJSON_GetObjectItem(item, "path");
compressFilter->path = ParsePath(pathNode);
cJSON *excludePathNode = cJSON_GetObjectItem(item, "exclude_path");
compressFilter->excludePath = ParsePath(excludePathNode);
cJSON *rulesNode = cJSON_GetObjectItem(item, "rules_orignal");
compressFilter->rules = ParseRules(rulesNode);
cJSON *expandRulesNode = cJSON_GetObjectItem(item, "rules_union");
compressFilter->expandRules = ParseRules(expandRulesNode);
compressFilters_.emplace_back(compressFilter);
}
return true;
}
vector<string> CompressionParser::ParseRules(const cJSON *rulesNode)
{
vector<string> res;
if (!rulesNode || !cJSON_IsObject(rulesNode)) {
cerr << "Error: ParseRules fail.";
return res;
}
for (cJSON *item = rulesNode->child; item; item = item->next) {
if (!item || !cJSON_IsArray(item)) {
continue;
}
string name(item->string);
res.emplace_back("\"" + name + "\":" + ParseJsonStr(item));
}
return res;
}
vector<string> CompressionParser::ParsePath(const cJSON *pathNode)
{
vector<string> res;
if (!pathNode || !cJSON_IsArray(pathNode)) {
cerr << "Error: ParsePath fail.";
return res;
}
for (cJSON *item = pathNode->child; item; item = item->next) {
if (!item || !cJSON_IsString(item)) {
continue;
}
res.emplace_back(item->valuestring);
}
return res;
}
string CompressionParser::ParseJsonStr(const cJSON *node)
{
if (!node) {
return "";
}
char *jsonString = cJSON_Print(node);
string res(jsonString);
free(jsonString);
return res;
}
bool CompressionParser::LoadImageTranscoder()
{
#ifdef __WIN32
if (!handle_) {
handle_ = LoadLibrary(TEXT(extensionPath_.c_str()));
if (!handle_) {
cerr << "Error: open '" << extensionPath_.c_str() << "' fail." << endl;
cerr << "Error: LoadLibrary failed with error: " << GetLastError() << endl;
return false;
}
}
#else
if (!handle_) {
handle_ = dlopen(extensionPath_.c_str(), RTLD_LAZY);
if (!handle_) {
cerr << "Error: open '" << extensionPath_.c_str() << "' fail." << endl;
cerr << "Error: dlopen failed with error: " << dlerror() << endl;
return false;
}
}
#endif
return true;
}
bool CompressionParser::SetTranscodeOptions(const string &optionJson)
{
if (!handle_) {
cerr << "Error: handle_ is nullptr."<< endl;
return false;
}
#ifdef __WIN32
ISetTranscodeOptions iSetTranscodeOptions = (ISetTranscodeOptions)GetProcAddress(handle_, "SetTranscodeOptions");
#else
ISetTranscodeOptions iSetTranscodeOptions = (ISetTranscodeOptions)dlsym(handle_, "SetTranscodeOptions");
#endif
if (!iSetTranscodeOptions) {
cerr << "Error: Failed to get the 'SetTranscodeOptions'."<< endl;
return false;
}
bool ret = (*iSetTranscodeOptions)(optionJson);
if (!ret) {
cerr << "Error: SetTranscodeOptions failed."<< endl;
return false;
}
return true;
}
TranscodeError CompressionParser::TranscodeImages(const string &imagePath, string &outputPath, TranscodeResult &result)
{
if (!handle_) {
cerr << "Error: handle_ is nullptr."<< endl;
return TranscodeError::LOAD_COMPRESS_FAILED;
}
#ifdef __WIN32
ITranscodeImages iTranscodeImages = (ITranscodeImages)GetProcAddress(handle_, "Transcode");
#else
ITranscodeImages iTranscodeImages = (ITranscodeImages)dlsym(handle_, "Transcode");
#endif
if (!iTranscodeImages) {
cerr << "Error: Failed to get the 'Transcode'."<< endl;
return TranscodeError::LOAD_COMPRESS_FAILED;
}
TranscodeError ret = (*iTranscodeImages)(imagePath, outputPath, result);
if (ret != TranscodeError::SUCCESS) {
auto iter = ERRORCODEMAP.find(ret);
if (iter != ERRORCODEMAP.end()) {
cerr << "Error: TranscodeImages failed, error message: " << iter->second << endl;
} else {
cerr << "Error: TranscodeImages failed." << endl;
}
return ret;
}
return TranscodeError::SUCCESS;
}
bool CompressionParser::CheckPath(const string &src, const vector<string> &paths)
{
return any_of(paths.begin(), paths.end(), [src](const auto &iter) {
return iter == src;
});
}
bool CompressionParser::IsInPath(const string &src, const shared_ptr<CompressFilter> &compressFilter)
{
return CheckPath(src, compressFilter->path);
}
bool CompressionParser::IsInExcludePath(const string &src, const shared_ptr<CompressFilter> &compressFilter)
{
return CheckPath(src, compressFilter->excludePath);
}
string CompressionParser::GetOptionsString(const shared_ptr<CompressFilter> &compressFilter, int type)
{
if (compressFilter->rules.size() == 0 || compressFilter->expandRules.size() == 0) {
return "{" + compressFilter->method + "}";
}
string res = "{" + compressFilter->method + "}";
switch (type) {
case OPT_TYPE_ONE:
if (compressFilter->rules.size() == OPT_SIZE_ONE) {
res.append(compressFilter->rules[0]).append("}");
} else {
res.append(compressFilter->rules[0]).append(",").append(compressFilter->rules[1]).append("}");
}
break;
case OPT_TYPE_TWO:
if (compressFilter->rules.size() == OPT_SIZE_ONE) {
res.append(compressFilter->rules[0]).append("}");
} else if (compressFilter->expandRules.size() == OPT_SIZE_TWO) {
res.append(compressFilter->rules[0]).append(",").append(compressFilter->expandRules[1]).append("}");
}
break;
case OPT_TYPE_THREE:
if (compressFilter->expandRules.size() == OPT_SIZE_ONE) {
res.append(compressFilter->expandRules[0]).append("}");
} else if (compressFilter->rules.size() == OPT_SIZE_TWO) {
res.append(compressFilter->expandRules[0]).append(",").append(compressFilter->rules[1]).append("}");
}
break;
default:
break;
}
return res;
}
bool CompressionParser::CopyAndTranscode(const string &src, string &dst)
{
if (!mediaSwitch_) {
return ResourceUtil::CopyFileInner(src, dst);
}
auto index = dst.find_last_of(SEPARATOR_FILE);
if (index == string::npos) {
cerr << "Error: invalid output path." << NEW_LINE_PATH << dst << endl;
return false;
}
string output = dst.substr(0, index);
TranscodeResult result = {0, 0, 0, 0, 0};
for (const auto &compressFilter : compressFilters_) {
if (!IsInPath(src, compressFilter)) {
continue;
}
if (IsInExcludePath(src, compressFilter)) {
if (!SetTranscodeOptions(GetOptionsString(compressFilter, OPT_TYPE_ONE)) ||
TranscodeImages(src, output, result) != TranscodeError::SUCCESS) {
continue;
}
dst = output;
return true;
} else {
if ((!SetTranscodeOptions(GetOptionsString(compressFilter, OPT_TYPE_THREE)) ||
TranscodeImages(src, output, result) != TranscodeError::SUCCESS) &&
(!SetTranscodeOptions(GetOptionsString(compressFilter, OPT_TYPE_TWO)) ||
TranscodeImages(src, output, result) != TranscodeError::SUCCESS)) {
continue;
}
dst = output;
return true;
}
}
return ResourceUtil::CopyFileInner(src, dst);
}
}
}
}
+14 -6
View File
@@ -18,6 +18,7 @@
#include "file_entry.h"
#include "resource_util.h"
#include "restool_errors.h"
#include "compression_parser.h"
namespace OHOS {
namespace Global {
@@ -37,23 +38,29 @@ uint32_t GenericCompiler::CompileSingleFile(const FileInfo &fileInfo)
return RESTOOL_SUCCESS;
}
if (!CopyFile(fileInfo)) {
string output = "";
if (!CopyMediaFile(fileInfo, output)) {
return RESTOOL_ERROR;
}
if (!PostFile(fileInfo)) {
if (!PostMediaFile(fileInfo, output)) {
return RESTOOL_ERROR;
}
return RESTOOL_SUCCESS;
}
bool GenericCompiler::PostFile(const FileInfo &fileInfo)
bool GenericCompiler::PostMediaFile(const FileInfo &fileInfo, const std::string &output)
{
ResourceItem resourceItem(fileInfo.filename, fileInfo.keyParams, type_);
resourceItem.SetFilePath(fileInfo.filePath);
resourceItem.SetLimitKey(fileInfo.limitKey);
string data = fileInfo.filename;
auto index = output.find_last_of(SEPARATOR_FILE);
if (index == string::npos) {
cerr << "Error: invalid output path." << NEW_LINE_PATH << output << endl;
return false;
}
string data = output.substr(index + 1);
data = moduleName_ + SEPARATOR + RESOURCES_DIR + SEPARATOR + \
fileInfo.limitKey + SEPARATOR + fileInfo.fileCluster + SEPARATOR + data;
if (!resourceItem.SetData(reinterpret_cast<const int8_t *>(data.c_str()), data.length())) {
@@ -75,13 +82,14 @@ bool GenericCompiler::IsIgnore(const FileInfo &fileInfo) const
return ResourceUtil::FileExist(GetOutputFilePath(fileInfo));
}
bool GenericCompiler::CopyFile(const FileInfo &fileInfo) const
bool GenericCompiler::CopyMediaFile(const FileInfo &fileInfo, std::string &output)
{
string outputFolder = GetOutputFolder(fileInfo);
if (!ResourceUtil::CreateDirs(outputFolder)) {
return false;
}
return ResourceUtil::CopyFleInner(fileInfo.filePath, GetOutputFilePath(fileInfo));
output = GetOutputFilePath(fileInfo);
return CompressionParser::GetCompressionParser()->CopyAndTranscode(fileInfo.filePath, output);
}
}
}
+2 -2
View File
@@ -263,7 +263,7 @@ bool ResourceAppend::ScanFile(const FileInfo &fileInfo, const string &outputPath
if (ResourceAppend::IsBaseIdDefined(fileInfo)) {
cout << "Warning: id_defined.json does not compile to generate intermediate files" << endl;
FileEntry::FilePath outPath(outputPath);
return ResourceUtil::CopyFleInner(fileInfo.filePath, outPath.Append(ID_DEFINED_FILE).GetPath());
return ResourceUtil::CopyFileInner(fileInfo.filePath, outPath.Append(ID_DEFINED_FILE).GetPath());
}
unique_ptr<IResourceCompiler> resourceCompiler =
@@ -565,7 +565,7 @@ bool ResourceAppend::LoadResourceItemFromMem(const char buffer[], int32_t length
continue;
}
if (!ResourceUtil::CopyFleInner(filePathStr, outPath.Append(data).GetPath())) {
if (!ResourceUtil::CopyFileInner(filePathStr, outPath.Append(data).GetPath())) {
return false;
}
continue;
+3 -2
View File
@@ -304,7 +304,8 @@ uint32_t ResourcePack::CopyRawFileOrResFileImpl(const string &src, const string
cerr << "Warning: '" << entry->GetFilePath().GetPath() << "' is defined repeatedly." << endl;
continue;
}
if (!ResourceUtil::CopyFleInner(entry->GetFilePath().GetPath(), subPath)) {
string path = entry->GetFilePath().GetPath();
if (!CompressionParser::GetCompressionParser()->CopyAndTranscode(path, subPath)) {
return RESTOOL_ERROR;
}
}
@@ -509,7 +510,7 @@ bool ResourcePack::CopyIcon(string &dataPath, const string &idName, string &file
cerr << "Error: Create Dirs fail '" << dstDir << "'."<< endl;
return false;
}
if (!ResourceUtil::CopyFleInner(source, dst)) {
if (!ResourceUtil::CopyFileInner(source, dst)) {
cerr << "Error: copy file fail from '" << source << "' to '" << dst << "'." << endl;
return false;
}
+1 -1
View File
@@ -209,7 +209,7 @@ ResType ResourceUtil::GetResTypeFromString(const string &type)
return ResType::INVALID_RES_TYPE;
}
bool ResourceUtil::CopyFleInner(const string &src, const string &dst)
bool ResourceUtil::CopyFileInner(const string &src, const string &dst)
{
return FileEntry::CopyFileInner(src, dst);
}