enable heif hardware decode

Signed-off-by: JillFred <14623865@qq.com>
This commit is contained in:
JillFred 2024-05-22 15:23:02 +08:00
parent 879a7fd8c4
commit c49538fcda
45 changed files with 5510 additions and 6 deletions

View File

@ -56,6 +56,9 @@ group("plugins") {
if (enable_jpeg_hw_decode) {
deps += [ "frameworks/innerkitsimpl/test/unittest/jpeg_hw_decode/demo:jpeg_hw_decoder_demo" ]
}
if (enable_heif_hw_decode) {
deps += [ "frameworks/innerkitsimpl/test/unittest/heif_hw_decode/demo:heif_hw_decoder_demo" ]
}
}
}

View File

@ -42,6 +42,7 @@
"drivers_interface_codec",
"drivers_interface_display",
"drivers_peripheral_display",
"drivers_peripheral_codec",
"hdf_core",
"memmgr_override",
"libjpeg-turbo",

View File

@ -183,7 +183,7 @@ ohos_unittest("imagesourcetest") {
]
if (enable_heif_hw_decode) {
source += [ "$image_subsystem/frameworks/innerkitsimpl/test/unittest/image_source_test/image_source_heif_test.cpp" ]
sources += [ "$image_subsystem/frameworks/innerkitsimpl/test/unittest/image_source_test/image_source_heif_test.cpp" ]
}
if (DUAL_ADAPTER) {
@ -1189,6 +1189,60 @@ ohos_unittest("jpeg_hw_decoder_test") {
]
}
ohos_unittest("heif_hw_decoder_test") {
module_out_path = module_output_path
sources = [
"${image_subsystem}/plugins/common/libs/image/libextplugin/src/hardware/heif_hw_decoder.cpp",
"${image_subsystem}/plugins/common/libs/image/libextplugin/src/hardware/imagecodec/codec_state.cpp",
"${image_subsystem}/plugins/common/libs/image/libextplugin/src/hardware/imagecodec/format.cpp",
"${image_subsystem}/plugins/common/libs/image/libextplugin/src/hardware/imagecodec/image_codec.cpp",
"${image_subsystem}/plugins/common/libs/image/libextplugin/src/hardware/imagecodec/image_codec_buffer.cpp",
"${image_subsystem}/plugins/common/libs/image/libextplugin/src/hardware/imagecodec/image_codec_dfx.cpp",
"${image_subsystem}/plugins/common/libs/image/libextplugin/src/hardware/imagecodec/image_codec_list.cpp",
"${image_subsystem}/plugins/common/libs/image/libextplugin/src/hardware/imagecodec/image_decoder.cpp",
"${image_subsystem}/plugins/common/libs/image/libextplugin/src/hardware/imagecodec/msg_handle_loop.cpp",
"${image_subsystem}/plugins/common/libs/image/libextplugin/src/hardware/imagecodec/param_bundle.cpp",
"${image_subsystem}/plugins/common/libs/image/libextplugin/src/hardware/imagecodec/state_machine.cpp",
"${image_subsystem}/plugins/common/libs/image/libextplugin/src/hardware/imagecodec/type_converter.cpp",
"unittest/heif_hw_decode/common/mock_heif_hw_decode_flow.cpp",
"unittest/heif_hw_decode/unittest/heif_hw_decoder_test.cpp",
]
include_dirs = [
"${image_subsystem}/frameworks/innerkitsimpl/test/unittest/heif_hw_decode/common/",
"${image_subsystem}/frameworks/innerkitsimpl/utils/include/",
"${image_subsystem}/interfaces/innerkits/include/",
"${image_subsystem}/plugins/common/libs/image/libextplugin/include/",
"${image_subsystem}/../../../base/hiviewdfx/hilog/interfaces/native/innerkits/include/",
"${image_subsystem}/../../../base/hiviewdfx/hitrace/interfaces/native/innerkits/include/hitrace_meter/",
"${image_subsystem}/../../../base/startup/init/interfaces/innerkits/include/",
"${image_subsystem}/../../../commonlibrary/c_utils/base/include/",
"${image_subsystem}/../../../drivers/hdf_core/interfaces/inner_api/",
"${image_subsystem}/../../../drivers/peripheral/codec/interfaces/include/",
"${image_subsystem}/../../../foundation/graphic/graphic_surface/interfaces/inner_api/surface/",
"${image_subsystem}/../../../foundation/resourceschedule/qos_manager/interfaces/inner_api",
"foundation/multimedia/image_framework/interfaces/innerkits/include",
]
deps = [
"${image_subsystem}/frameworks/innerkitsimpl/utils:image_utils",
"//third_party/googletest:gtest_main",
]
external_deps = [
"c_utils:utils",
"drivers_interface_codec:codec_idl_headers",
"drivers_interface_codec:libcodec_proxy_3.0",
"graphic_surface:surface",
"hdf_core:libhdi",
"hilog:libhilog",
"hitrace:hitrace_meter",
"init:libbegetutil",
"ipc:ipc_core",
"openmax:libopenmax_static",
"qos_manager:qos",
]
resource_config_file = "$image_subsystem/test/resource/image/ohos_test.xml"
}
ohos_unittest("heif_parser_test") {
module_out_path = module_output_path
@ -1212,7 +1266,6 @@ ohos_unittest("heif_parser_test") {
"${image_subsystem}/../../../foundation/graphic/graphic_surface/interfaces/inner_api/surface/",
"${image_subsystem}/../../../foundation/multimedia/image_framework/interfaces/innerkits/include",
"${image_subsystem}/../../../foundation/resourceschedule/qos_manager/interfaces/inner_api/",
"${image_subsystem}/../../../third_party/openmax/api/1.1.2/",
]
deps = [
@ -1232,6 +1285,7 @@ ohos_unittest("heif_parser_test") {
"hitrace:hitrace_meter",
"init:libbegetutil",
"ipc:ipc_core",
"openmax:libopenmax_static",
"qos_manager:qos",
]
@ -1752,7 +1806,12 @@ group("unittest") {
}
if (enable_heif_hw_decode) {
deps += [ ":heifyuvtest" ]
deps += [
":heif_hw_decoder_test",
":heifyuvtest",
]
}
}
################################################

View File

@ -0,0 +1,93 @@
/*
* Copyright (c) 2024 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "command_parser.h"
#include <getopt.h>
#include <iostream>
namespace OHOS::ImagePlugin {
using namespace std;
enum ShortOption {
OPT_UNKONWN = 0,
OPT_HELP,
OPT_INPUT = 'i',
OPT_PIXEL_FORMAT
};
static struct option g_longOptions[] = {
{"help", no_argument, nullptr, static_cast<int>(ShortOption::OPT_HELP)},
{"in", required_argument, nullptr, static_cast<int>(ShortOption::OPT_INPUT)},
{"pixelFormat", required_argument, nullptr, static_cast<int>(ShortOption::OPT_PIXEL_FORMAT)},
{nullptr, no_argument, nullptr, static_cast<int>(ShortOption::OPT_UNKONWN)},
};
void ShowUsage()
{
std::cout << "Heif Hardware decode Demo Options:" << std::endl;
std::cout << " --help help info." << std::endl;
std::cout << " -i, --in full file path for input file." << std::endl;
std::string pixFmtHelpInfo = "pixel format of output. 0 is NV12, 1 is NV21, 2 is NV12_10bit, 3 is NV21_10bit";
std::cout << " --pixelFormat " << pixFmtHelpInfo << std::endl;
}
CommandOpt Parse(int argc, char *argv[])
{
CommandOpt opt;
int c;
while ((c = getopt_long(argc, argv, "i:", g_longOptions, nullptr)) != -1) {
switch (static_cast<ShortOption>(c)) {
case ShortOption::OPT_HELP:
ShowUsage();
break;
case ShortOption::OPT_INPUT:
opt.inputPath = string(optarg);
break;
case ShortOption::OPT_PIXEL_FORMAT:
opt.pixelFormat = static_cast<UserPixelFormat>(stol(optarg));
break;
default:
break;
}
}
return opt;
}
void CommandOpt::Print() const
{
std::string pixelFmtDesc = "unknown";
switch (pixelFormat) {
case UserPixelFormat::NV12:
pixelFmtDesc = "NV12";
break;
case UserPixelFormat::NV21:
pixelFmtDesc = "NV21";
break;
case UserPixelFormat::NV12_10bit:
pixelFmtDesc = "NV12_10bit";
break;
case UserPixelFormat::NV21_10bit:
pixelFmtDesc = "NV21_10bit";
break;
default:
break;
}
std::cout << "=========================== OPT INFO ===========================" << endl;
std::cout << " inputPath : " << inputPath << endl;
std::cout << " pixelFormat : " << pixelFmtDesc << endl;
std::cout << "=================================================================" << endl;
}
} // OHOS::ImagePlugin

View File

@ -0,0 +1,39 @@
/*
* Copyright (c) 2024 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef HEIF_COMMAND_PARSER_H
#define HEIF_COMMAND_PARSER_H
#include <string>
namespace OHOS::ImagePlugin {
enum class UserPixelFormat {
NV12 = 0,
NV21 = 1,
NV12_10bit = 2,
NV21_10bit = 3
};
struct CommandOpt {
UserPixelFormat pixelFormat;
std::string inputPath;
void Print() const;
};
CommandOpt Parse(int argc, char *argv[]);
void ShowUsage();
} // OHOS::ImagePlugin
#endif // HEIF_COMMAND_PARSER_H

View File

@ -0,0 +1,221 @@
/*
* Copyright (c) 2024 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "mock_heif_hw_decode_flow.h"
#include "hardware/imagecodec/image_codec_log.h"
#include "media_errors.h"
#include <map>
#include <fstream>
#include <filesystem>
#include <dirent.h>
#include <sys/stat.h>
#include <algorithm>
namespace OHOS::ImagePlugin {
using namespace std;
void HeifHwDecoderFlow::InputParser::SplitString(const std::string& src, char sep, std::vector<std::string>& vec)
{
vec.clear();
string::size_type startPos = 0;
while (true) {
string::size_type endPos = src.find_first_of(sep, startPos);
if (endPos == string::npos) {
break;
}
vec.emplace_back(src.substr(startPos, endPos - startPos));
startPos = endPos + 1;
}
if (startPos != string::npos) {
vec.emplace_back(src.substr(startPos));
}
}
std::string HeifHwDecoderFlow::InputParser::JoinPath(const std::string& base, const std::string& append)
{
return (filesystem::path(base) / append).string();
}
bool HeifHwDecoderFlow::InputParser::ParseGridInfo(GridInfo& gridInfo)
{
// source_ demo:
// 1. has grid: 3072x4096_grid_512x512_6x8
// 2. no grid: 3072x4096_nogrid
string baseDir = filesystem::path(source_).filename().string();
vector<string> vec;
SplitString(baseDir, MAIN_SEP, vec);
IF_TRUE_RETURN_VAL_WITH_MSG(vec.size() < MIN_MAIN_SEG_CNT, false,
"invalid source: %{public}s", source_.c_str());
vector<string> vecTmp;
SplitString(vec[DISPLAY_SIZE], SUB_SEP, vecTmp);
IF_TRUE_RETURN_VAL_WITH_MSG(vecTmp.size() != SUB_SEG_CNT, false, "invalid source: %{public}s", source_.c_str());
gridInfo.displayWidth = static_cast<uint32_t>(stol(vecTmp[HORIZONTAL].c_str()));
gridInfo.displayHeight = static_cast<uint32_t>(stol(vecTmp[VERTICAL].c_str()));
if (vec[GRID_FLAG].find(NO_GRID_INDICATOR) != string::npos) {
gridInfo.enableGrid = false;
gridInfo.cols = 0;
gridInfo.rows = 0;
gridInfo.tileWidth = 0;
gridInfo.tileHeight = 0;
} else {
IF_TRUE_RETURN_VAL_WITH_MSG(vec.size() < MAX_MAIN_SEG_CNT, false,
"invalid source: %{public}s", source_.c_str());
gridInfo.enableGrid = true;
SplitString(vec[TILE_SIZE], SUB_SEP, vecTmp);
IF_TRUE_RETURN_VAL_WITH_MSG(vecTmp.size() != SUB_SEG_CNT, false,
"invalid source: %{public}s", source_.c_str());
gridInfo.tileWidth = static_cast<uint32_t>(stol(vecTmp[HORIZONTAL].c_str()));
gridInfo.tileHeight = static_cast<uint32_t>(stol(vecTmp[VERTICAL].c_str()));
SplitString(vec[GRID_SIZE], SUB_SEP, vecTmp);
IF_TRUE_RETURN_VAL_WITH_MSG(vecTmp.size() != SUB_SEG_CNT, false,
"invalid source: %{public}s", source_.c_str());
gridInfo.cols = static_cast<uint32_t>(stol(vecTmp[HORIZONTAL].c_str()));
gridInfo.rows = static_cast<uint32_t>(stol(vecTmp[VERTICAL].c_str()));
}
return true;
}
void HeifHwDecoderFlow::InputParser::FindXpsAndIFrameFile()
{
DIR *dirp = opendir(source_.c_str());
IF_TRUE_RETURN_VOID_WITH_MSG(dirp == nullptr, "failed to open: %{public}s, errno=%{public}d",
source_.c_str(), errno);
struct dirent *dp;
while ((dp = readdir(dirp)) != nullptr) {
if (strcmp(dp->d_name, ".") == 0 || strcmp(dp->d_name, "..") == 0) {
continue;
}
string path = JoinPath(source_, dp->d_name);
struct stat st{};
if (stat(path.c_str(), &st) != 0 || !S_ISREG(st.st_mode)) {
continue;
}
string fileName(dp->d_name);
if (fileName.find(XPS_INDICATOR) != string::npos) {
xpsFile_ = path;
} else if (fileName.find(I_FRAME_INDICATOR) != string::npos) {
iFrameFile_.emplace_back(path);
}
}
closedir(dirp);
}
bool HeifHwDecoderFlow::InputParser::ReadFileToVec(const string& filePath, vector<vector<uint8_t>>& inputs)
{
ifstream ifs(filePath, ios::binary);
IF_TRUE_RETURN_VAL_WITH_MSG(!ifs.is_open(), false, "failed to open file: %{public}s", filePath.c_str());
ifs.seekg(0, ifstream::end);
size_t fileSize = static_cast<size_t>(ifs.tellg());
ifs.seekg(0, ifstream::beg);
vector<uint8_t> vec(fileSize);
ifs.read(reinterpret_cast<char*>(vec.data()), static_cast<streamsize>(fileSize));
ifs.close();
inputs.emplace_back(vec);
return true;
}
int HeifHwDecoderFlow::InputParser::ExtractIFrameNum(const string& filePath)
{
string fileName = filesystem::path(filePath).filename().string();
string::size_type pos = fileName.find(I_FRAME_INDICATOR);
if (pos == string::npos) {
return -1;
}
return stoi(fileName.substr(pos + string(I_FRAME_INDICATOR).size()));
}
bool HeifHwDecoderFlow::InputParser::ReadInput(vector<vector<uint8_t>>& inputs)
{
FindXpsAndIFrameFile();
IF_TRUE_RETURN_VAL_WITH_MSG(xpsFile_.empty(), false, "no xps file in %{public}s", source_.c_str());
IF_TRUE_RETURN_VAL_WITH_MSG(iFrameFile_.empty(), false, "no iframe file in %{public}s", source_.c_str());
IF_TRUE_RETURN_VAL_WITH_MSG(!ReadFileToVec(xpsFile_, inputs), false,
"failed to read xps file: %{public}s", xpsFile_.c_str());
std::sort(iFrameFile_.begin(), iFrameFile_.end(), [](const string& a, const string& b) {
return ExtractIFrameNum(a) < ExtractIFrameNum(b);
});
for (const string& one : iFrameFile_) {
IF_TRUE_RETURN_VAL_WITH_MSG(!ReadFileToVec(one, inputs), false,
"failed to read iframe file: %{public}s", one.c_str());
}
return true;
}
HeifHwDecoderFlow::~HeifHwDecoderFlow()
{
output_ = nullptr;
}
bool HeifHwDecoderFlow::Run(const CommandOpt& opt)
{
bool ret = PrepareInput(opt.inputPath);
ret = ret && AllocOutput(opt.pixelFormat);
ret = ret && DoDecode();
if (ret) {
LOGI("demo succeed");
} else {
LOGE("demo failed");
}
return ret;
}
bool HeifHwDecoderFlow::PrepareInput(const std::string& inputPath)
{
InputParser parser(inputPath);
bool ret = parser.ParseGridInfo(gridInfo_);
ret = ret && parser.ReadInput(input_);
return ret;
}
bool HeifHwDecoderFlow::AllocOutput(UserPixelFormat userPixelFormat)
{
static const map<UserPixelFormat, GraphicPixelFormat> userPixelFmtToGraphicPixelFmt = {
{ UserPixelFormat::NV12, GRAPHIC_PIXEL_FMT_YCBCR_420_SP },
{ UserPixelFormat::NV21, GRAPHIC_PIXEL_FMT_YCRCB_420_SP },
{ UserPixelFormat::NV12_10bit, GRAPHIC_PIXEL_FMT_YCBCR_P010 },
{ UserPixelFormat::NV21_10bit, GRAPHIC_PIXEL_FMT_YCRCB_P010 },
};
auto iter = userPixelFmtToGraphicPixelFmt.find(userPixelFormat);
if (iter == userPixelFmtToGraphicPixelFmt.end()) {
LOGE("unsupported pixel format: %{public}d", static_cast<int>(userPixelFormat));
return false;
}
GraphicPixelFormat pixelFmt = iter->second;
output_ = hwDecoder_.AllocateOutputBuffer(gridInfo_.displayWidth, gridInfo_.displayHeight, pixelFmt);
if (output_ == nullptr) {
LOGE("failed to alloc output");
return false;
}
return true;
}
bool HeifHwDecoderFlow::DoDecode()
{
uint32_t ret = hwDecoder_.DoDecode(gridInfo_, input_, output_);
if (ret != Media::SUCCESS) {
LOGE("failed to decode");
return false;
}
return true;
}
} // namespace OHOS::ImagePlugin

View File

@ -0,0 +1,78 @@
/*
* Copyright (c) 2024 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef HEIF_HW_DECODER_DEMO_H
#define HEIF_HW_DECODER_DEMO_H
#include <vector>
#include "command_parser.h"
#include "hardware/heif_hw_decoder.h"
namespace OHOS::ImagePlugin {
class HeifHwDecoderFlow {
public:
HeifHwDecoderFlow() = default;
~HeifHwDecoderFlow();
bool Run(const CommandOpt& opt);
private:
class InputParser {
public:
explicit InputParser(const std::string& inputPath) : source_(inputPath) {}
~InputParser() = default;
bool ParseGridInfo(GridInfo& gridInfo);
bool ReadInput(std::vector<std::vector<uint8_t>>& inputs);
private:
void FindXpsAndIFrameFile();
static void SplitString(const std::string& src, char sep, std::vector<std::string>& vec);
static std::string JoinPath(const std::string& base, const std::string& append);
static bool ReadFileToVec(const std::string& filePath, std::vector<std::vector<uint8_t>>& inputs);
static int ExtractIFrameNum(const std::string& filePath);
static constexpr char MAIN_SEP = '_';
static constexpr size_t MIN_MAIN_SEG_CNT = 2;
static constexpr size_t MAX_MAIN_SEG_CNT = 4;
static constexpr char SUB_SEP = 'x';
static constexpr size_t SUB_SEG_CNT = 2;
static constexpr char NO_GRID_INDICATOR[] = "nogrid";
static constexpr char XPS_INDICATOR[] = "_hevc_xps";
static constexpr char I_FRAME_INDICATOR[] = "_hevc_I";
enum MainSeg {
DISPLAY_SIZE = 0,
GRID_FLAG,
TILE_SIZE,
GRID_SIZE
};
enum SubSeg {
HORIZONTAL = 0,
VERTICAL
};
std::string source_;
std::string xpsFile_;
std::vector<std::string> iFrameFile_;
};
private:
bool PrepareInput(const std::string& inputPath);
bool AllocOutput(UserPixelFormat userPixelFormat);
bool DoDecode();
private:
GridInfo gridInfo_;
std::vector<std::vector<uint8_t>> input_;
sptr<SurfaceBuffer> output_;
HeifHardwareDecoder hwDecoder_;
};
} // OHOS::ImagePlugin
#endif // HEIF_HW_DECODER_DEMO_H

View File

@ -0,0 +1,76 @@
# Copyright (c) 2023 Huawei Device Co., Ltd.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import("//build/ohos.gni")
import("//foundation/multimedia/image_framework/ide/image_decode_config.gni")
ohos_executable("heif_hw_decoder_demo") {
install_enable = false
subsystem_name = "multimedia"
part_name = "image_framework"
if (!use_clang_android && !use_clang_ios) {
sanitize = {
cfi = true
cfi_cross_dso = true
debug = false
}
}
if (build_variant == "root") {
defines = [ "BUILD_ENG_VERSION" ]
}
sources = [
"${image_subsystem}/frameworks/innerkitsimpl/test/unittest/heif_hw_decode/common/command_parser.cpp",
"${image_subsystem}/frameworks/innerkitsimpl/test/unittest/heif_hw_decode/common/mock_heif_hw_decode_flow.cpp",
"${image_subsystem}/plugins/common/libs/image/libextplugin/src/hardware/heif_hw_decoder.cpp",
"${image_subsystem}/plugins/common/libs/image/libextplugin/src/hardware/imagecodec/codec_state.cpp",
"${image_subsystem}/plugins/common/libs/image/libextplugin/src/hardware/imagecodec/format.cpp",
"${image_subsystem}/plugins/common/libs/image/libextplugin/src/hardware/imagecodec/image_codec.cpp",
"${image_subsystem}/plugins/common/libs/image/libextplugin/src/hardware/imagecodec/image_codec_buffer.cpp",
"${image_subsystem}/plugins/common/libs/image/libextplugin/src/hardware/imagecodec/image_codec_dfx.cpp",
"${image_subsystem}/plugins/common/libs/image/libextplugin/src/hardware/imagecodec/image_codec_list.cpp",
"${image_subsystem}/plugins/common/libs/image/libextplugin/src/hardware/imagecodec/image_decoder.cpp",
"${image_subsystem}/plugins/common/libs/image/libextplugin/src/hardware/imagecodec/msg_handle_loop.cpp",
"${image_subsystem}/plugins/common/libs/image/libextplugin/src/hardware/imagecodec/param_bundle.cpp",
"${image_subsystem}/plugins/common/libs/image/libextplugin/src/hardware/imagecodec/state_machine.cpp",
"${image_subsystem}/plugins/common/libs/image/libextplugin/src/hardware/imagecodec/type_converter.cpp",
"heif_hw_decoder_demo.cpp",
]
include_dirs = [
"${image_subsystem}/frameworks/innerkitsimpl/test/unittest/heif_hw_decode/common/",
"${image_subsystem}/interfaces/innerkits/include/",
"${image_subsystem}/plugins/common/libs/image/libextplugin/include/",
]
deps = [ "${image_subsystem}/interfaces/innerkits:image_native" ]
external_deps = [
"c_utils:utils",
"drivers_interface_codec:codec_idl_headers",
"drivers_interface_codec:libcodec_proxy_3.0",
"drivers_peripheral_codec:libcodec_hdi_omx_client",
"graphic_surface:surface",
"hdf_core:libhdi",
"hilog:libhilog",
"init:libbegetutil",
"ipc:ipc_core",
"openmax:libopenmax_static",
"qos_manager:qos",
]
if (build_variant == "root") {
external_deps += [ "hitrace:hitrace_meter" ]
}
}

View File

@ -0,0 +1,33 @@
/*
* Copyright (c) 2024 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "mock_heif_hw_decode_flow.h"
namespace OHOS::ImagePlugin {
using namespace std;
extern "C" {
int main(int argc, char *argv[])
{
CommandOpt opt = Parse(argc, argv);
if (!opt.inputPath.empty()) {
opt.Print();
HeifHwDecoderFlow demo;
(void)demo.Run(opt);
}
return 0;
}
}
}

View File

@ -0,0 +1,206 @@
/*
* Copyright (c) 2021 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <gtest/gtest.h>
#include "hardware/heif_hw_decoder.h"
#include "image_system_properties.h"
#include "mock_heif_hw_decode_flow.h"
#include "media_errors.h" // foundation/multimedia/image_framework/interfaces/innerkits/include/
namespace OHOS::Media {
using namespace testing::ext;
using namespace OHOS::ImagePlugin;
class HeifHwDecoderTest : public testing::Test {
public:
static constexpr char TEST_HEIF_IMG_NO_GRID[] = "/data/local/tmp/image/heif_test/1024x1024_nogrid";
static constexpr char TEST_HEIF_IMG_WITH_GRID[] = "/data/local/tmp/image/heif_test/1024x1024_grid_512x512_2x2";
};
HWTEST_F(HeifHwDecoderTest, AllocOutputBufferWithInvalidSize, TestSize.Level1)
{
HeifHardwareDecoder testObj;
sptr<SurfaceBuffer> output = testObj.AllocateOutputBuffer(
0, 512, static_cast<int32_t>(GRAPHIC_PIXEL_FMT_YCBCR_420_SP));
ASSERT_TRUE(output == nullptr);
}
HWTEST_F(HeifHwDecoderTest, AllocOutputBufferWithInvalidPixelFmt, TestSize.Level1)
{
HeifHardwareDecoder testObj;
sptr<SurfaceBuffer> output = testObj.AllocateOutputBuffer(
512, 512, static_cast<int32_t>(GRAPHIC_PIXEL_FMT_RGBA_8888));
ASSERT_TRUE(output == nullptr);
}
HWTEST_F(HeifHwDecoderTest, AllocOutputBufferOk, TestSize.Level1)
{
HeifHardwareDecoder testObj;
sptr<SurfaceBuffer> output = testObj.AllocateOutputBuffer(
1024, 512, static_cast<int32_t>(GRAPHIC_PIXEL_FMT_YCBCR_420_SP));
ASSERT_TRUE(output != nullptr);
}
HWTEST_F(HeifHwDecoderTest, DoDecodeWithInvalidInputs, TestSize.Level1)
{
HeifHardwareDecoder testObj;
GridInfo gridInfo = {
.displayWidth = 1024,
.displayHeight = 512,
.enableGrid = false,
.cols = 0,
.rows = 0,
.tileWidth = 0,
.tileHeight = 0
};
std::vector<std::vector<uint8_t>> inputs;
sptr<SurfaceBuffer> output = testObj.AllocateOutputBuffer(gridInfo.displayWidth, gridInfo.displayHeight,
static_cast<int32_t>(GRAPHIC_PIXEL_FMT_YCBCR_420_SP));
uint32_t ret = testObj.DoDecode(gridInfo, inputs, output);
ASSERT_TRUE(ret != Media::SUCCESS);
}
HWTEST_F(HeifHwDecoderTest, DoDecodeWithInvalidOutputBuffer, TestSize.Level1)
{
HeifHardwareDecoder testObj;
GridInfo gridInfo = {
.displayWidth = 1024,
.displayHeight = 512,
.enableGrid = false,
.cols = 0,
.rows = 0,
.tileWidth = 0,
.tileHeight = 0
};
std::vector<std::vector<uint8_t>> inputs;
inputs.emplace_back(std::vector<uint8_t>(1));
inputs.emplace_back(std::vector<uint8_t>(1));
sptr<SurfaceBuffer> output = nullptr;
uint32_t ret = testObj.DoDecode(gridInfo, inputs, output);
ASSERT_TRUE(ret != Media::SUCCESS);
}
HWTEST_F(HeifHwDecoderTest, DoDecodeWithInvalidGridInfo1, TestSize.Level1)
{
HeifHardwareDecoder testObj;
GridInfo gridInfo = {
.displayWidth = 512,
.displayHeight = 512,
.enableGrid = true,
.cols = 0,
.rows = 0,
.tileWidth = 0,
.tileHeight = 0
};
std::vector<std::vector<uint8_t>> inputs;
inputs.emplace_back(std::vector<uint8_t>(1));
inputs.emplace_back(std::vector<uint8_t>(1));
sptr<SurfaceBuffer> output = testObj.AllocateOutputBuffer(gridInfo.displayWidth, gridInfo.displayHeight,
static_cast<int32_t>(GRAPHIC_PIXEL_FMT_YCBCR_420_SP));
uint32_t ret = testObj.DoDecode(gridInfo, inputs, output);
ASSERT_TRUE(ret != Media::SUCCESS);
}
HWTEST_F(HeifHwDecoderTest, DoDecodeWithInvalidGridInfo2, TestSize.Level1)
{
HeifHardwareDecoder testObj;
GridInfo gridInfo = {
.displayWidth = 1024,
.displayHeight = 1024,
.enableGrid = true,
.cols = 1,
.rows = 2,
.tileWidth = 512,
.tileHeight = 512
};
std::vector<std::vector<uint8_t>> inputs;
inputs.emplace_back(std::vector<uint8_t>(1));
inputs.emplace_back(std::vector<uint8_t>(1));
sptr<SurfaceBuffer> output = testObj.AllocateOutputBuffer(gridInfo.displayWidth, gridInfo.displayHeight,
static_cast<int32_t>(GRAPHIC_PIXEL_FMT_YCBCR_420_SP));
uint32_t ret = testObj.DoDecode(gridInfo, inputs, output);
ASSERT_TRUE(ret != Media::SUCCESS);
}
HWTEST_F(HeifHwDecoderTest, DoDecodeWithInvalidGridInfo3, TestSize.Level1)
{
HeifHardwareDecoder testObj;
GridInfo gridInfo = {
.displayWidth = 512,
.displayHeight = 512,
.enableGrid = true,
.cols = 1,
.rows = 1,
.tileWidth = 512,
.tileHeight = 0
};
std::vector<std::vector<uint8_t>> inputs;
inputs.emplace_back(std::vector<uint8_t>(1));
inputs.emplace_back(std::vector<uint8_t>(1));
sptr<SurfaceBuffer> output = testObj.AllocateOutputBuffer(gridInfo.displayWidth, gridInfo.displayHeight,
static_cast<int32_t>(GRAPHIC_PIXEL_FMT_YCBCR_420_SP));
uint32_t ret = testObj.DoDecode(gridInfo, inputs, output);
ASSERT_TRUE(ret != Media::SUCCESS);
}
HWTEST_F(HeifHwDecoderTest, DoDecodeWithInvalidGridInfo4, TestSize.Level1)
{
HeifHardwareDecoder testObj;
GridInfo gridInfo = {
.displayWidth = 513,
.displayHeight = 512,
.enableGrid = true,
.cols = 1,
.rows = 1,
.tileWidth = 512,
.tileHeight = 512
};
std::vector<std::vector<uint8_t>> inputs;
inputs.emplace_back(std::vector<uint8_t>(1));
inputs.emplace_back(std::vector<uint8_t>(1));
sptr<SurfaceBuffer> output = testObj.AllocateOutputBuffer(gridInfo.displayWidth, gridInfo.displayHeight,
static_cast<int32_t>(GRAPHIC_PIXEL_FMT_YCBCR_420_SP));
uint32_t ret = testObj.DoDecode(gridInfo, inputs, output);
ASSERT_TRUE(ret != Media::SUCCESS);
}
HWTEST_F(HeifHwDecoderTest, DoDecodeOkNoGrid, TestSize.Level1)
{
bool ret = true;
if (ImageSystemProperties::GetHardWareDecodeEnabled()) {
CommandOpt opt = {
.pixelFormat = UserPixelFormat::NV21,
.inputPath = TEST_HEIF_IMG_NO_GRID
};
HeifHwDecoderFlow testObj;
ret = testObj.Run(opt);
}
ASSERT_TRUE(ret);
}
HWTEST_F(HeifHwDecoderTest, DoDecodeOkWithGrid, TestSize.Level1)
{
bool ret = true;
if (ImageSystemProperties::GetHardWareDecodeEnabled()) {
CommandOpt opt = {
.pixelFormat = UserPixelFormat::NV21,
.inputPath = TEST_HEIF_IMG_WITH_GRID
};
HeifHwDecoderFlow testObj;
ret = testObj.Run(opt);
}
ASSERT_TRUE(ret);
}
} // namespace OHOS::Media

View File

@ -455,7 +455,7 @@ ohos_shared_library("heifimpl") {
external_deps += [
"c_utils:utils",
"drivers_interface_codec:codec_idl_headers",
"drivers_interface_codec:libcodec_proxy_2.0",
"drivers_interface_codec:libcodec_proxy_3.0",
"drivers_peripheral_codec:libcodec_hdi_omx_client",
"ffmpeg:libohosffmpeg",
"hdf_core:libhdi",

View File

@ -0,0 +1,118 @@
/*
* Copyright (c) 2024 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef HEIF_HW_DECODER_H
#define HEIF_HW_DECODER_H
#include <cinttypes>
#include <mutex>
#include <condition_variable>
#include <vector>
#include <list>
#include "imagecodec/image_codec.h"
namespace OHOS {
namespace ImagePlugin {
struct GridInfo {
uint32_t displayWidth = 0;
uint32_t displayHeight = 0;
bool enableGrid = false;
uint32_t cols = 0;
uint32_t rows = 0;
uint32_t tileWidth = 0;
uint32_t tileHeight = 0;
bool IsValid() const;
};
class HeifHardwareDecoder {
public:
HeifHardwareDecoder();
~HeifHardwareDecoder();
sptr<SurfaceBuffer> AllocateOutputBuffer(uint32_t width, uint32_t height, int32_t pixelFmt);
uint32_t DoDecode(const GridInfo& gridInfo, std::vector<std::vector<uint8_t>>& inputs, sptr<SurfaceBuffer>& output);
private:
class HeifDecoderCallback : public ImageCodecCallback {
public:
HeifDecoderCallback(HeifHardwareDecoder* heifDecoder);
void OnError(ImageCodecError err) override;
void OnOutputFormatChanged(const Format &format) override;
void OnInputBufferAvailable(uint32_t index, std::shared_ptr<ImageCodecBuffer> buffer) override;
void OnOutputBufferAvailable(uint32_t index, std::shared_ptr<ImageCodecBuffer> buffer) override;
HeifHardwareDecoder* heifDecoder_;
};
private:
struct RawYuvCopyInfo {
uint8_t* yStart = 0;
uint8_t* uvStart = 0;
uint32_t stride = 0;
uint32_t yStride = 0;
uint32_t yOffset = 0;
uint32_t uvOffset = 0;
};
private:
static bool GetUvPlaneOffsetFromSurfaceBuffer(sptr<SurfaceBuffer>& surfaceBuffer, uint64_t& offset);
bool IsHardwareDecodeSupported(const GridInfo& gridInfo);
bool SetCallbackForDecoder();
bool ConfigureDecoder(const GridInfo& gridInfo, sptr<SurfaceBuffer>& output);
bool SetOutputBuffer(const GridInfo& gridInfo, sptr<SurfaceBuffer> output);
bool WaitForOmxToReturnInputBuffer(uint32_t& bufferId, std::shared_ptr<ImageCodecBuffer>& buffer);
int32_t PrepareInputCodecBuffer(const std::vector<std::vector<uint8_t>>& inputs, size_t inputIndex,
std::shared_ptr<ImageCodecBuffer>& buffer);
void SendInputBufferLoop(const std::vector<std::vector<uint8_t>>& inputs);
bool WaitForOmxToReturnOutputBuffer(uint32_t& bufferId, std::shared_ptr<ImageCodecBuffer>& buffer);
void AssembleOutput(uint32_t outputIndex, std::shared_ptr<ImageCodecBuffer>& buffer);
static uint32_t CalculateDirtyLen(uint32_t displayLen, uint32_t gridLen, uint32_t totalGrid, uint32_t curGrid);
static bool CopyRawYuvData(const RawYuvCopyInfo& src, const RawYuvCopyInfo& dst,
uint32_t dirtyWidth, uint32_t dirtyHeight);
void ReceiveOutputBufferLoop();
static int64_t GetTimestampInUs();
void ReleaseDecoder();
void SignalError();
bool HasError();
void Reset();
void FlushOutput();
std::string GetOutputPixelFmtDesc();
void DumpOutput();
private:
static constexpr int32_t BUFFER_CIRCULATE_TIMEOUT_IN_MS = 1000;
static constexpr uint32_t SAMPLE_RATIO_FOR_YUV420_SP = 2;
static constexpr size_t MIN_SIZE_OF_INPUT = 2;
std::shared_ptr<ImageCodec> heifDecoderImpl_;
sptr<SurfaceBuffer> output_;
uint64_t uvOffsetForOutput_;
GridInfo gridInfo_;
std::mutex errMtx_;
bool hasErr_ = false;
std::mutex inputMtx_;
std::condition_variable inputCond_;
std::list<std::pair<uint32_t, std::shared_ptr<ImageCodecBuffer>>> inputList_;
std::mutex outputMtx_;
std::condition_variable outputCond_;
std::list<std::pair<uint32_t, std::shared_ptr<ImageCodecBuffer>>> outputList_;
std::thread releaseThread_;
};
} // namespace ImagePlugin
} // namespace OHOS
#endif // HEIF_HW_DECODER_H

View File

@ -0,0 +1,55 @@
/*
* Copyright (c) 2024 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef IMAGE_CODEC_FORMAT_H
#define IMAGE_CODEC_FORMAT_H
#include <string>
#include <unordered_map>
#include <any>
#include <memory>
namespace OHOS::ImagePlugin {
class Format {
public:
Format() = default;
~Format() = default;
explicit Format(const Format& src);
Format& operator=(const Format& src);
bool ContainKey(const std::string &key) const;
template<typename T>
void SetValue(const std::string &key, const T &value)
{
m_items[key] = value;
}
template<typename T>
bool GetValue(const std::string &key, T &value) const
{
const auto it = m_items.find(key);
if (it == m_items.end()) {
return false;
}
value = std::any_cast<T>(it->second);
return true;
}
private:
std::unordered_map<std::string, std::any> m_items;
};
}; // namespace OHOS::ImagePlugin
#endif // IMAGE_CODEC_FORMAT_H

View File

@ -0,0 +1,28 @@
/*
* Copyright (c) 2024 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef IMAGE_HDI_DEFINE_H
#define IMAGE_HDI_DEFINE_H
#include "v3_0/codec_types.h"
#include "v3_0/icodec_callback.h"
#include "v3_0/icodec_component.h"
#include "v3_0/icodec_component_manager.h"
namespace OHOS::ImagePlugin {
namespace HdiCodecNamespace = OHOS::HDI::Codec::V3_0;
} // namespace OHOS::ImagePlugin
#endif // IMAGE_HDI_DEFINE_H

View File

@ -0,0 +1,423 @@
/*
* Copyright (c) 2024 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef IMAGE_CODEC_H
#define IMAGE_CODEC_H
#include <queue>
#include <array>
#include <functional>
#include "securec.h"
#include "OMX_Component.h" // third_party/openmax/api/1.1.2
#include "param_bundle.h"
#include "image_codec_buffer.h"
#include "image_codec_common.h"
#include "format.h"
#include "state_machine.h"
#include "type_converter.h"
#include "codec_omx_ext.h"
#include "hdi_define.h"
namespace OHOS::ImagePlugin {
inline constexpr int TIME_RATIO_S_TO_MS = 1000;
inline constexpr double US_TO_MS = 1000.0;
inline constexpr double US_TO_S = 1000000.0;
inline constexpr uint32_t STRIDE_ALIGNMENT = 32;
inline uint32_t GetYuv420Size(uint32_t w, uint32_t h)
{
return w * h * 3 / 2; // 3: nom of ratio, 2: denom of ratio
}
class ImageCodec : protected StateMachine {
public:
static std::shared_ptr<ImageCodec> Create();
std::string GetComponentName() const { return componentName_; }
int32_t SetCallback(const std::shared_ptr<ImageCodecCallback> &callback);
int32_t Configure(const Format &format);
int32_t QueueInputBuffer(uint32_t index);
int32_t ReleaseOutputBuffer(uint32_t index);
int32_t GetInputFormat(Format& format);
int32_t GetOutputFormat(Format& format);
int32_t Start();
int32_t Release();
int32_t GetOutputBufferUsage(uint64_t& usage);
int32_t SetOutputBuffer(sptr<SurfaceBuffer> output);
protected:
enum MsgWhat : MsgType {
INIT,
SET_CALLBACK,
CONFIGURE,
START,
GET_INPUT_FORMAT,
GET_OUTPUT_FORMAT,
QUEUE_INPUT_BUFFER,
RELEASE_OUTPUT_BUFFER,
RELEASE,
GET_OUTPUT_BUFFER_USAGE,
SET_OUTPUT_BUFFER,
INNER_MSG_BEGIN = 1000,
CODEC_EVENT,
OMX_EMPTY_BUFFER_DONE,
OMX_FILL_BUFFER_DONE,
CHECK_IF_STUCK,
FORCE_SHUTDOWN,
};
enum BufferOperationMode {
KEEP_BUFFER,
RESUBMIT_BUFFER,
FREE_BUFFER,
};
enum BufferOwner {
OWNED_BY_US = 0,
OWNED_BY_USER = 1,
OWNED_BY_OMX = 2,
OWNER_CNT = 3,
};
struct PortInfo {
uint32_t width;
uint32_t height;
OMX_VIDEO_CODINGTYPE codingType;
std::optional<PixelFmt> pixelFmt;
double frameRate;
std::optional<uint32_t> inputBufSize;
std::optional<uint32_t> bufferCnt;
};
struct BufferInfo {
BufferInfo() : lastOwnerChangeTime(std::chrono::steady_clock::now()) {}
bool isInput = true;
BufferOwner owner = OWNED_BY_US;
std::chrono::time_point<std::chrono::steady_clock> lastOwnerChangeTime;
uint32_t bufferId = 0;
std::shared_ptr<HdiCodecNamespace::OmxCodecBuffer> omxBuffer;
sptr<SurfaceBuffer> surfaceBuffer;
std::shared_ptr<ImageCodecBuffer> imgCodecBuffer;
void CleanUpUnusedInfo();
void BeginCpuAccess();
void EndCpuAccess();
bool IsValidFrame() const;
void Dump(const std::string& prefix, bool dumpMode) const;
private:
void Dump(const std::string& prefix) const;
void DumpSurfaceBuffer(const std::string& prefix) const;
void DumpLinearBuffer(const std::string& prefix) const;
static constexpr char DUMP_PATH[] = "/data/misc/imagecodecdump";
};
protected:
ImageCodec(OMX_VIDEO_CODINGTYPE codingType, bool isEncoder);
~ImageCodec() override;
static const char* ToString(MsgWhat what);
static const char* ToString(BufferOwner owner);
void ReplyErrorCode(MsgId id, int32_t err);
void PrintAllBufferInfo();
std::array<uint32_t, OWNER_CNT> CountOwner(bool isInput);
void ChangeOwner(BufferInfo& info, BufferOwner newOwner);
void UpdateInputRecord(const BufferInfo& info, std::chrono::time_point<std::chrono::steady_clock> now);
void UpdateOutputRecord(const BufferInfo& info, std::chrono::time_point<std::chrono::steady_clock> now);
// configure
virtual int32_t OnConfigure(const Format &format) = 0;
bool GetPixelFmtFromUser(const Format &format);
static std::optional<double> GetFrameRateFromUser(const Format &format);
int32_t SetVideoPortInfo(OMX_DIRTYPE portIndex, const PortInfo& info);
virtual int32_t UpdateInPortFormat() = 0;
virtual int32_t UpdateOutPortFormat() = 0;
virtual void UpdateColorAspects() {}
void PrintPortDefinition(const OMX_PARAM_PORTDEFINITIONTYPE& def);
int32_t SetFrameRateAdaptiveMode(const Format &format);
int32_t SetProcessName(const Format &format);
virtual int32_t ReConfigureOutputBufferCnt() = 0;
virtual uint64_t OnGetOutputBufferUsage() = 0;
virtual int32_t OnSetOutputBuffer(sptr<SurfaceBuffer> output) = 0;
// start
virtual bool ReadyToStart() = 0;
virtual int32_t AllocateBuffersOnPort(OMX_DIRTYPE portIndex) = 0;
virtual void UpdateFormatFromSurfaceBuffer() = 0;
int32_t GetPortDefinition(OMX_DIRTYPE portIndex, OMX_PARAM_PORTDEFINITIONTYPE& def);
int32_t AllocateSurfaceBuffers(OMX_DIRTYPE portIndex, sptr<SurfaceBuffer> output = nullptr);
int32_t AllocateHardwareBuffers(OMX_DIRTYPE portIndex);
std::shared_ptr<HdiCodecNamespace::OmxCodecBuffer> SurfaceBufferToOmxBuffer(
const sptr<SurfaceBuffer>& surfaceBuffer);
std::shared_ptr<HdiCodecNamespace::OmxCodecBuffer> DynamicSurfaceBufferToOmxBuffer();
virtual int32_t SubmitAllBuffersOwnedByUs() = 0;
virtual int32_t SubmitOutputBuffersToOmxNode() { return IC_ERR_UNSUPPORT; }
BufferInfo* FindBufferInfoByID(OMX_DIRTYPE portIndex, uint32_t bufferId);
std::optional<size_t> FindBufferIndexByID(OMX_DIRTYPE portIndex, uint32_t bufferId);
// input buffer circulation
virtual void NotifyUserToFillThisInBuffer(BufferInfo &info);
virtual void OnQueueInputBuffer(const MsgInfo &msg, BufferOperationMode mode);
void OnQueueInputBuffer(BufferOperationMode mode, BufferInfo* info);
int32_t NotifyOmxToEmptyThisInBuffer(BufferInfo& info);
virtual void OnOMXEmptyBufferDone(uint32_t bufferId, BufferOperationMode mode) = 0;
// output buffer circulation
int32_t NotifyOmxToFillThisOutBuffer(BufferInfo &info);
void OnOMXFillBufferDone(const HdiCodecNamespace::OmxCodecBuffer& omxBuffer, BufferOperationMode mode);
void OnOMXFillBufferDone(BufferOperationMode mode, BufferInfo& info, size_t bufferIdx);
void NotifyUserOutBufferAvaliable(BufferInfo &info);
void OnReleaseOutputBuffer(const MsgInfo &msg, BufferOperationMode mode);
// // stop/release
void ReclaimBuffer(OMX_DIRTYPE portIndex, BufferOwner owner, bool erase = false);
bool IsAllBufferOwnedByUs(OMX_DIRTYPE portIndex);
bool IsAllBufferOwnedByUs();
void EraseOutBuffersOwnedByUs();
void ClearBufferPool(OMX_DIRTYPE portIndex);
virtual void EraseBufferFromPool(OMX_DIRTYPE portIndex, size_t i) = 0;
void FreeOmxBuffer(OMX_DIRTYPE portIndex, const BufferInfo& info);
// template
template <typename T>
static inline void InitOMXParam(T& param)
{
(void)memset_s(&param, sizeof(T), 0x0, sizeof(T));
param.nSize = sizeof(T);
param.nVersion.s.nVersionMajor = 1;
}
template <typename T>
static inline void InitOMXParamExt(T& param)
{
(void)memset_s(&param, sizeof(T), 0x0, sizeof(T));
param.size = sizeof(T);
param.version.s.nVersionMajor = 1;
}
template <typename T>
bool GetParameter(uint32_t index, T& param, bool isCfg = false)
{
int8_t* p = reinterpret_cast<int8_t*>(&param);
std::vector<int8_t> inVec(p, p + sizeof(T));
std::vector<int8_t> outVec;
int32_t ret = isCfg ? compNode_->GetConfig(index, inVec, outVec) :
compNode_->GetParameter(index, inVec, outVec);
if (ret != HDF_SUCCESS) {
return false;
}
if (outVec.size() != sizeof(T)) {
return false;
}
ret = memcpy_s(&param, sizeof(T), outVec.data(), outVec.size());
if (ret != EOK) {
return false;
}
return true;
}
template <typename T>
bool SetParameter(uint32_t index, const T& param, bool isCfg = false)
{
const int8_t* p = reinterpret_cast<const int8_t*>(&param);
std::vector<int8_t> inVec(p, p + sizeof(T));
int32_t ret = isCfg ? compNode_->SetConfig(index, inVec) :
compNode_->SetParameter(index, inVec);
if (ret != HDF_SUCCESS) {
return false;
}
return true;
}
protected:
bool isEncoder_;
OMX_VIDEO_CODINGTYPE codingType_;
uint32_t componentId_ = 0;
std::string componentName_;
std::string compUniqueStr_;
bool debugMode_ = false;
bool dumpMode_ = false;
sptr<HdiCodecNamespace::ICodecCallback> compCb_ = nullptr;
sptr<HdiCodecNamespace::ICodecComponent> compNode_ = nullptr;
sptr<HdiCodecNamespace::ICodecComponentManager> compMgr_ = nullptr;
std::shared_ptr<ImageCodecCallback> callback_;
PixelFmt configuredFmt_;
BufferRequestConfig requestCfg_;
std::shared_ptr<Format> configFormat_;
std::shared_ptr<Format> inputFormat_;
std::shared_ptr<Format> outputFormat_;
std::vector<BufferInfo> inputBufferPool_;
std::vector<BufferInfo> outputBufferPool_;
bool isBufferCirculating_ = false;
bool inputPortEos_ = false;
bool outputPortEos_ = false;
struct TotalCntAndCost {
uint64_t totalCnt = 0;
uint64_t totalCostUs = 0;
};
std::array<std::array<TotalCntAndCost, OWNER_CNT>, OWNER_CNT> inputHoldTimeRecord_;
std::chrono::time_point<std::chrono::steady_clock> firstInTime_;
uint64_t inTotalCnt_ = 0;
std::array<std::array<TotalCntAndCost, OWNER_CNT>, OWNER_CNT> outputHoldTimeRecord_;
std::chrono::time_point<std::chrono::steady_clock> firstOutTime_;
TotalCntAndCost outRecord_;
std::unordered_map<int64_t, std::chrono::time_point<std::chrono::steady_clock>> inTimeMap_;
static constexpr char BUFFER_ID[] = "buffer-id";
static constexpr uint32_t WAIT_FENCE_MS = 1000;
private:
struct BaseState : State {
protected:
BaseState(ImageCodec *codec, const std::string &stateName,
BufferOperationMode inputMode = KEEP_BUFFER, BufferOperationMode outputMode = KEEP_BUFFER)
: State(stateName), codec_(codec), inputMode_(inputMode), outputMode_(outputMode) {}
void OnMsgReceived(const MsgInfo &info) override;
void ReplyErrorCode(MsgId id, int32_t err);
void OnCodecEvent(const MsgInfo &info);
virtual void OnCodecEvent(HdiCodecNamespace::CodecEventType event, uint32_t data1, uint32_t data2);
void OnGetFormat(const MsgInfo &info);
virtual void OnShutDown(const MsgInfo &info) = 0;
void OnCheckIfStuck(const MsgInfo &info);
void OnForceShutDown(const MsgInfo &info);
void OnStateExited() override { codec_->stateGeneration_++; }
protected:
ImageCodec *codec_;
BufferOperationMode inputMode_;
BufferOperationMode outputMode_;
};
struct UninitializedState : BaseState {
explicit UninitializedState(ImageCodec *codec) : BaseState(codec, "Uninitialized") {}
private:
void OnStateEntered() override;
void OnMsgReceived(const MsgInfo &info) override;
int32_t OnAllocateComponent(const std::string &name);
void OnShutDown(const MsgInfo &info) override;
};
struct InitializedState : BaseState {
explicit InitializedState(ImageCodec *codec) : BaseState(codec, "Initialized") {}
private:
void OnStateEntered() override;
void ProcessShutDownFromRunning();
void OnMsgReceived(const MsgInfo &info) override;
void OnSetCallBack(const MsgInfo &info);
void OnConfigure(const MsgInfo &info);
void OnGetOutputBufferUsage(const MsgInfo &info);
void OnSetOutputBuffer(const MsgInfo &info);
void OnStart(const MsgInfo &info);
void OnShutDown(const MsgInfo &info) override;
};
struct StartingState : BaseState {
explicit StartingState(ImageCodec *codec) : BaseState(codec, "Starting") {}
private:
void OnStateEntered() override;
void OnStateExited() override;
void OnMsgReceived(const MsgInfo &info) override;
int32_t AllocateBuffers();
void OnCodecEvent(HdiCodecNamespace::CodecEventType event, uint32_t data1, uint32_t data2) override;
void OnShutDown(const MsgInfo &info) override;
void ReplyStartMsg(int32_t errCode);
bool hasError_ = false;
};
struct RunningState : BaseState {
explicit RunningState(ImageCodec *codec) : BaseState(codec, "Running", RESUBMIT_BUFFER, RESUBMIT_BUFFER) {}
private:
void OnStateEntered() override;
void OnMsgReceived(const MsgInfo &info) override;
void OnCodecEvent(HdiCodecNamespace::CodecEventType event, uint32_t data1, uint32_t data2) override;
void OnShutDown(const MsgInfo &info) override;
};
struct OutputPortChangedState : BaseState {
explicit OutputPortChangedState(ImageCodec *codec)
: BaseState(codec, "OutputPortChanged", RESUBMIT_BUFFER, FREE_BUFFER) {}
private:
void OnStateEntered() override;
void OnMsgReceived(const MsgInfo &info) override;
void OnCodecEvent(HdiCodecNamespace::CodecEventType event, uint32_t data1, uint32_t data2) override;
void OnShutDown(const MsgInfo &info) override;
void HandleOutputPortDisabled();
void HandleOutputPortEnabled();
};
struct StoppingState : BaseState {
explicit StoppingState(ImageCodec *codec) : BaseState(codec, "Stopping"),
omxNodeInIdleState_(false),
omxNodeIsChangingToLoadedState_(false) {}
private:
void OnStateEntered() override;
void OnMsgReceived(const MsgInfo &info) override;
void OnCodecEvent(HdiCodecNamespace::CodecEventType event, uint32_t data1, uint32_t data2) override;
void OnShutDown(const MsgInfo &info) override;
void ChangeStateIfWeOwnAllBuffers();
void ChangeOmxNodeToLoadedState(bool forceToFreeBuffer);
bool omxNodeInIdleState_;
bool omxNodeIsChangingToLoadedState_;
};
class HdiCallback : public HdiCodecNamespace::ICodecCallback {
public:
explicit HdiCallback(ImageCodec* codec) : codec_(codec) { }
virtual ~HdiCallback() = default;
int32_t EventHandler(HdiCodecNamespace::CodecEventType event, const HdiCodecNamespace::EventInfo& info);
int32_t EmptyBufferDone(int64_t appData, const HdiCodecNamespace::OmxCodecBuffer& buffer);
int32_t FillBufferDone(int64_t appData, const HdiCodecNamespace::OmxCodecBuffer& buffer);
private:
ImageCodec* codec_;
};
private:
int32_t DoSyncCall(MsgWhat msgType, std::function<void(ParamSP)> oper);
int32_t DoSyncCallAndGetReply(MsgWhat msgType, std::function<void(ParamSP)> oper, ParamSP &reply);
int32_t InitWithName(const std::string &name);
void ReleaseComponent();
void CleanUpOmxNode();
void ChangeOmxToTargetState(HdiCodecNamespace::CodecStateType &state,
HdiCodecNamespace::CodecStateType targetState);
bool RollOmxBackToLoaded();
int32_t ForceShutdown(int32_t generation);
void SignalError(ImageCodecError err);
void DeferMessage(const MsgInfo &info);
void ProcessDeferredMessages();
void ReplyToSyncMsgLater(const MsgInfo& msg);
bool GetFirstSyncMsgToReply(MsgInfo& msg);
private:
static constexpr size_t MAX_IMAGE_CODEC_BUFFER_SIZE = 8192 * 4096 * 4; // 8K RGBA
static constexpr uint32_t THREE_SECONDS_IN_US = 3'000'000;
static constexpr double FRAME_RATE_COEFFICIENT = 65536.0;
std::shared_ptr<UninitializedState> uninitializedState_;
std::shared_ptr<InitializedState> initializedState_;
std::shared_ptr<StartingState> startingState_;
std::shared_ptr<RunningState> runningState_;
std::shared_ptr<OutputPortChangedState> outputPortChangedState_;
std::shared_ptr<StoppingState> stoppingState_;
int32_t stateGeneration_ = 0;
bool isShutDownFromRunning_ = false;
bool notifyCallerAfterShutdownComplete_ = false;
bool hasFatalError_ = false;
std::list<MsgInfo> deferredQueue_;
std::map<MsgType, std::queue<std::pair<MsgId, ParamSP>>> syncMsgToReply_;
}; // class ImageCodec
} // namespace OHOS::ImagePlugin
#endif // IMAGE_CODEC_H

View File

@ -0,0 +1,99 @@
/*
* Copyright (c) 2024 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef IMAGE_CODEC_BUFFER_H
#define IMAGE_CODEC_BUFFER_H
#include "surface_type.h" // foundation/graphic/graphic_surface/interfaces/inner_api/surface/surface_type.h
#include "surface_buffer.h" // foundation/graphic/graphic_surface/interfaces/inner_api/surface/surface_buffer.h
namespace OHOS::ImagePlugin {
enum YuvSemiPlanarArrangement {
PLANE_Y = 0,
PLANE_U,
PLANE_V,
PLANE_BUTT
};
class ImageCodecBuffer {
public:
static std::shared_ptr<ImageCodecBuffer> CreateDmaBuffer(int fd, int32_t capacity, int32_t stride);
static std::shared_ptr<ImageCodecBuffer> CreateSurfaceBuffer(const BufferRequestConfig &config);
static std::shared_ptr<ImageCodecBuffer> CreateSurfaceBuffer(sptr<SurfaceBuffer> surface);
virtual ~ImageCodecBuffer() = default;
void GetBufferCirculateInfo(int64_t& pts, uint32_t& flag, uint32_t& size, uint32_t& offset) const
{
pts = pts_;
flag = flag_;
size = size_;
offset = offset_;
}
void SetBufferCirculateInfo(int64_t pts, uint32_t flag, uint32_t size, uint32_t offset)
{
pts_ = pts;
flag_ = flag;
size_ = size;
offset_ = offset;
}
uint32_t GetBufferFlag() const { return flag_; }
int64_t GetPts() const { return pts_; }
int32_t GetCapacity() const { return capacity_; }
int32_t GetStride() const { return stride_; }
virtual bool Init() = 0;
virtual int32_t GetFileDescriptor() = 0;
virtual sptr<SurfaceBuffer> GetSurfaceBuffer() = 0;
virtual uint8_t* GetAddr() = 0;
protected:
ImageCodecBuffer() = default;
protected:
int64_t pts_ = 0;
uint32_t size_ = 0;
uint32_t offset_ = 0;
uint32_t flag_; // OMX_BUFFERFLAG_X defined in OMX_Core.h
int32_t capacity_ = 0;
int32_t stride_ = 0;
};
class ImageDmaBuffer : public ImageCodecBuffer {
public:
ImageDmaBuffer(int fd, int32_t capacity, int32_t stride);
~ImageDmaBuffer();
bool Init() override { return true; }
int32_t GetFileDescriptor() override { return fd_; }
sptr<SurfaceBuffer> GetSurfaceBuffer() override { return nullptr; }
uint8_t* GetAddr() override;
private:
int32_t fd_ = -1;
uint8_t* addr_ = nullptr;
};
class ImageSurfaceBuffer : public ImageCodecBuffer {
public:
explicit ImageSurfaceBuffer(const BufferRequestConfig &config);
explicit ImageSurfaceBuffer(sptr<SurfaceBuffer> surface);
~ImageSurfaceBuffer();
bool Init() override;
int32_t GetFileDescriptor() override;
uint8_t* GetAddr() override;
sptr<SurfaceBuffer> GetSurfaceBuffer() override;
private:
BufferRequestConfig config_;
sptr<SurfaceBuffer> surfaceBuffer_ = nullptr;
uint8_t* addr_ = nullptr;
};
} // namespace OHOS::ImagePlugin
#endif

View File

@ -0,0 +1,64 @@
/*
* Copyright (c) 2024 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef IMAGE_CODEC_COMMON_H
#define IMAGE_CODEC_COMMON_H
#include "surface_buffer.h" // foundation/graphic/graphic_surface/interfaces/inner_api/surface/surface_buffer.h
#include "surface_type.h" // foundation/graphic/graphic_surface/interfaces/inner_api/surface/surface_type.h
#include "format.h"
namespace OHOS::ImagePlugin {
enum ImageCodecError : int32_t {
IC_ERR_OK,
IC_ERR_SERVICE_DIED,
IC_ERR_INVALID_VAL,
IC_ERR_INVALID_OPERATION,
IC_ERR_INVALID_STATE,
IC_ERR_NO_MEMORY,
IC_ERR_UNSUPPORT,
IC_ERR_UNKNOWN
};
class ImageCodecCallback {
public:
virtual ~ImageCodecCallback() = default;
virtual void OnError(ImageCodecError err) = 0;
virtual void OnOutputFormatChanged(const Format &format) = 0;
virtual void OnInputBufferAvailable(uint32_t index, std::shared_ptr<ImageCodecBuffer> buffer) = 0;
virtual void OnOutputBufferAvailable(uint32_t index, std::shared_ptr<ImageCodecBuffer> buffer) = 0;
};
class ImageCodecDescriptionKey {
public:
static constexpr char WIDTH[] = "width";
static constexpr char HEIGHT[] = "height";
static constexpr char VIDEO_DISPLAY_WIDTH[] = "video_display_width";
static constexpr char VIDEO_DISPLAY_HEIGHT[] = "video_display_height";
static constexpr char MAX_INPUT_SIZE[] = "max_input_size";
static constexpr char INPUT_BUFFER_COUNT[] = "input_buffer_count";
static constexpr char OUTPUT_BUFFER_COUNT[] = "output_buffer_count";
static constexpr char ENABLE_HEIF_GRID[] = "enable_heif_grid";
static constexpr char PIXEL_FORMAT[] = "pixel_format";
static constexpr char RANGE_FLAG[] = "range_flag";
static constexpr char COLOR_PRIMARIES[] = "color_primaries";
static constexpr char TRANSFER_CHARACTERISTICS[] = "transfer_characteristics";
static constexpr char MATRIX_COEFFICIENTS[] = "matrix_coefficients";
static constexpr char FRAME_RATE[] = "frame_rate";
static constexpr char VIDEO_FRAME_RATE_ADAPTIVE_MODE[] = "video_frame_rate_adaptive_mode";
static constexpr char PROCESS_NAME[] = "process_name";
};
} // namespace OHOS::ImagePlugin
#endif // IMAGE_CODEC_COMMON_H

View File

@ -0,0 +1,26 @@
/*
* Copyright (c) 2024 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef IMAGE_CODEC_LIST_H
#define IMAGE_CODEC_LIST_H
#include "hdi_define.h"
namespace OHOS::ImagePlugin {
sptr<HdiCodecNamespace::ICodecComponentManager> GetManager();
std::vector<HdiCodecNamespace::CodecCompCapability> GetCapList();
} // namespace OHOS::ImagePlugin
#endif // IMAGE_CODEC_LIST_H

View File

@ -0,0 +1,141 @@
/*
* Copyright (c) 2024 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef IMAGE_CODEC_LOG_H
#define IMAGE_CODEC_LOG_H
#include <cinttypes>
#include <chrono>
#include "log_tags.h"
#include "hilog/log.h"
#ifdef BUILD_ENG_VERSION
#include "hitrace_meter.h"
#endif
inline constexpr OHOS::HiviewDFX::HiLogLabel IMAGE_CODEC_LABEL = {
LOG_CORE, LOG_TAG_DOMAIN_ID_PLUGIN, "HEIF_HW_DECODER"};
#ifdef __FILE_NAME__
#define FILENAME __FILE_NAME__
#else
#define FILENAME __FILE__
#endif
#define LOG_FMT "[%{public}s][%{public}s %{public}d] "
#define LOGE(x, ...) \
OHOS::HiviewDFX::HiLog::Error(IMAGE_CODEC_LABEL, LOG_FMT x, FILENAME, __FUNCTION__, __LINE__, ##__VA_ARGS__)
#define LOGW(x, ...) \
OHOS::HiviewDFX::HiLog::Warn(IMAGE_CODEC_LABEL, LOG_FMT x, FILENAME, __FUNCTION__, __LINE__, ##__VA_ARGS__)
#define LOGI(x, ...) \
OHOS::HiviewDFX::HiLog::Info(IMAGE_CODEC_LABEL, LOG_FMT x, FILENAME, __FUNCTION__, __LINE__, ##__VA_ARGS__)
#define LOGD(x, ...) \
OHOS::HiviewDFX::HiLog::Debug(IMAGE_CODEC_LABEL, LOG_FMT x, FILENAME, __FUNCTION__, __LINE__, ##__VA_ARGS__)
// for ImageCodecBuffer
#define HLOG_FMT "%{public}s[%{public}s][%{public}s %{public}d] "
#define HLOGE(x, ...) OHOS::HiviewDFX::HiLog::Error(IMAGE_CODEC_LABEL, HLOG_FMT x, compUniqueStr_.c_str(), \
currState_->GetName().c_str(), __FUNCTION__, __LINE__, ##__VA_ARGS__)
#define HLOGW(x, ...) OHOS::HiviewDFX::HiLog::Warn(IMAGE_CODEC_LABEL, HLOG_FMT x, compUniqueStr_.c_str(), \
currState_->GetName().c_str(), __FUNCTION__, __LINE__, ##__VA_ARGS__)
#define HLOGI(x, ...) OHOS::HiviewDFX::HiLog::Info(IMAGE_CODEC_LABEL, HLOG_FMT x, compUniqueStr_.c_str(), \
currState_->GetName().c_str(), __FUNCTION__, __LINE__, ##__VA_ARGS__)
#define HLOGD(x, ...) \
do { \
if (debugMode_) { \
OHOS::HiviewDFX::HiLog::Debug(IMAGE_CODEC_LABEL, HLOG_FMT x, compUniqueStr_.c_str(), \
currState_->GetName().c_str(), __FUNCTION__, __LINE__, ##__VA_ARGS__); \
} \
} while (0)
// for ImageCodecBuffer inner state
#define SLOGE(x, ...) OHOS::HiviewDFX::HiLog::Error(IMAGE_CODEC_LABEL, HLOG_FMT x, \
codec_->compUniqueStr_.c_str(), stateName_.c_str(), __FUNCTION__, __LINE__, ##__VA_ARGS__)
#define SLOGW(x, ...) OHOS::HiviewDFX::HiLog::Warn(IMAGE_CODEC_LABEL, HLOG_FMT x, \
codec_->compUniqueStr_.c_str(), stateName_.c_str(), __FUNCTION__, __LINE__, ##__VA_ARGS__)
#define SLOGI(x, ...) OHOS::HiviewDFX::HiLog::Info(IMAGE_CODEC_LABEL, HLOG_FMT x, \
codec_->compUniqueStr_.c_str(), stateName_.c_str(), __FUNCTION__, __LINE__, ##__VA_ARGS__)
#define SLOGD(x, ...) \
do { \
if (codec_->debugMode_) { \
OHOS::HiviewDFX::HiLog::Debug(IMAGE_CODEC_LABEL, HLOG_FMT x, \
codec_->compUniqueStr_.c_str(), stateName_.c_str(), __FUNCTION__, __LINE__, ##__VA_ARGS__); \
} \
} while (0)
#define IF_TRUE_RETURN_VAL(cond, val) \
do { \
if (cond) { \
return val; \
} \
} while (0)
#define IF_TRUE_RETURN_VAL_WITH_MSG(cond, val, msg, ...) \
do { \
if (cond) { \
LOGE(msg, ##__VA_ARGS__); \
return val; \
} \
} while (0)
#define IF_TRUE_RETURN_VOID(cond) \
do { \
if (cond) { \
return; \
} \
} while (0)
#define IF_TRUE_RETURN_VOID_WITH_MSG(cond, msg, ...) \
do { \
if (cond) { \
LOGE(msg, ##__VA_ARGS__); \
return; \
} \
} while (0)
#ifdef BUILD_ENG_VERSION
#ifdef H_SYSTRACE_TAG
#undef H_SYSTRACE_TAG
#endif
#define H_SYSTRACE_TAG HITRACE_TAG_ZMEDIA
#endif // BUILD_ENG_VERSION
class HeifPerfTracker {
public:
explicit HeifPerfTracker(std::string desc) : desc_(desc)
{
startTimeInUs_ = GetCurrentTimeInUs();
#ifdef BUILD_ENG_VERSION
StartTrace(H_SYSTRACE_TAG, desc);
#endif
}
~HeifPerfTracker()
{
#ifdef BUILD_ENG_VERSION
FinishTrace(H_SYSTRACE_TAG);
#endif
static constexpr float MILLISEC_TO_MICROSEC = 1000.0f;
int64_t timeSpanInUs = GetCurrentTimeInUs() - startTimeInUs_;
LOGI("%{public}s cost: %{public}.2f ms",
desc_.c_str(), static_cast<float>(timeSpanInUs / MILLISEC_TO_MICROSEC));
}
private:
int64_t GetCurrentTimeInUs()
{
auto now = std::chrono::steady_clock::now();
return std::chrono::duration_cast<std::chrono::microseconds>(now.time_since_epoch()).count();
}
int64_t startTimeInUs_;
std::string desc_;
};
#endif // IMAGE_CODEC_LOG_H

View File

@ -0,0 +1,61 @@
/*
* Copyright (c) 2024 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef IMAGE_DECODER_H
#define IMAGE_DECODER_H
#include "hardware/imagecodec/image_codec.h"
namespace OHOS::ImagePlugin {
class ImageDecoder : public ImageCodec {
public:
ImageDecoder();
private:
// configure
int32_t OnConfigure(const Format &format) override;
int32_t SetupPort(const Format &format);
int32_t UpdateInPortFormat() override;
int32_t UpdateOutPortFormat() override;
bool UpdateConfiguredFmt(OMX_COLOR_FORMATTYPE portFmt);
void UpdateColorAspects() override;
void UpdateDisplaySizeByCrop();
int32_t ReConfigureOutputBufferCnt() override;
uint64_t OnGetOutputBufferUsage() override;
int32_t OnSetOutputBuffer(sptr<SurfaceBuffer> output) override;
// start
int32_t AllocateBuffersOnPort(OMX_DIRTYPE portIndex) override;
void UpdateFormatFromSurfaceBuffer() override;
int32_t SubmitAllBuffersOwnedByUs() override;
int32_t SubmitOutputBuffersToOmxNode() override;
bool ReadyToStart() override;
// input buffer circulation
void OnOMXEmptyBufferDone(uint32_t bufferId, BufferOperationMode mode) override;
// output buffer circulation
uint64_t GetProducerUsage();
// stop/release
void EraseBufferFromPool(OMX_DIRTYPE portIndex, size_t i) override;
private:
static constexpr uint64_t BUFFER_MODE_REQUEST_USAGE =
BUFFER_USAGE_MEM_DMA | BUFFER_USAGE_VIDEO_DECODER | BUFFER_USAGE_CPU_READ | BUFFER_USAGE_MEM_MMZ_CACHE;
bool enableHeifGrid_ = false;
sptr<SurfaceBuffer> outputBuffer_;
};
} // namespace OHOS::ImagePlugin
#endif // IMAGE_DECODER_H

View File

@ -0,0 +1,66 @@
/*
* Copyright (c) 2024 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef IMAGE_CODEC_MSGQUEUETHREAD_H
#define IMAGE_CODEC_MSGQUEUETHREAD_H
#include <thread>
#include <mutex>
#include <condition_variable>
#include <functional>
#include <map>
#include <list>
#include "param_bundle.h"
namespace OHOS::ImagePlugin {
using MsgType = int32_t;
using MsgId = uint64_t;
struct MsgInfo {
MsgType type;
MsgId id;
ParamSP param;
};
class MsgHandleLoop {
protected:
MsgHandleLoop();
~MsgHandleLoop();
void SendAsyncMsg(MsgType type, const ParamSP &msg, uint32_t delayUs = 0);
bool SendSyncMsg(MsgType type, const ParamSP &msg, ParamSP &reply, uint32_t waitMs = 0);
virtual void OnMsgReceived(const MsgInfo &info) = 0;
void PostReply(MsgId id, const ParamSP &reply);
void Stop();
static constexpr MsgId ASYNC_MSG_ID = 0;
private:
void MainLoop();
MsgId GenerateMsgId();
using TimeUs = int64_t;
static TimeUs GetNowUs();
private:
std::thread m_thread;
std::mutex m_mtx;
bool m_threadNeedStop = false;
MsgId m_lastMsgId = 0;
std::map<TimeUs, MsgInfo> m_msgQueue; // msg will be sorted by timeUs
std::condition_variable m_threadCond;
std::mutex m_replyMtx;
std::map<MsgId, ParamSP> m_replies;
std::condition_variable m_replyCond;
};
} // namespace OHOS::ImagePlugin
#endif // IMAGE_CODEC_MSGQUEUETHREAD_H

View File

@ -0,0 +1,65 @@
/*
* Copyright (c) 2024 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef IMAGE_CODEC_PARAM_BUNDLE_H
#define IMAGE_CODEC_PARAM_BUNDLE_H
#include <string>
#include <unordered_map>
#include <any>
#include <memory>
#include <mutex>
namespace OHOS::ImagePlugin {
class ParamBundle;
using ParamSP = std::shared_ptr<ParamBundle>;
class ParamBundle {
public:
static ParamSP Create();
template<typename T>
void SetValue(const std::string &key, const T &value)
{
std::lock_guard<std::mutex> lock(m_mtx);
m_items[key] = value;
}
template<typename T>
bool GetValue(const std::string &key, T &value) const
{
std::lock_guard<std::mutex> lock(m_mtx);
const auto it = m_items.find(key);
if (it == m_items.end()) {
return false;
}
value = std::any_cast<T>(it->second);
return true;
}
ParamBundle(const ParamBundle &) = delete;
ParamBundle &operator=(const ParamBundle &) = delete;
ParamBundle(ParamBundle &&) = delete;
ParamBundle &operator=(ParamBundle &&) = delete;
private:
ParamBundle() = default;
~ParamBundle() = default;
mutable std::mutex m_mtx;
std::unordered_map<std::string, std::any> m_items;
};
} // namespace OHOS::ImagePlugin
#endif // IMAGE_CODEC_PARAM_BUNDLE_H

View File

@ -0,0 +1,53 @@
/*
* Copyright (c) 2024 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef IMAGE_CODEC_STATE_H
#define IMAGE_CODEC_STATE_H
#include <memory>
#include <string>
#include <utility>
#include "msg_handle_loop.h"
namespace OHOS::ImagePlugin {
class State {
public:
explicit State(std::string stateName) : stateName_(std::move(stateName)) {}
const std::string GetName() const { return stateName_; }
protected:
virtual ~State() = default;
virtual void OnStateEntered() {};
virtual void OnStateExited() {};
virtual void OnMsgReceived(const MsgInfo &info) = 0;
friend class StateMachine;
std::string stateName_;
};
class StateMachine : public MsgHandleLoop {
public:
StateMachine() = default;
protected:
virtual ~StateMachine() = default;
void ChangeStateTo(const std::shared_ptr<State> &targetState);
void OnMsgReceived(const MsgInfo &info) override;
std::shared_ptr<State> currState_;
};
} // namespace OHOS::ImagePlugin
#endif // IMAGE_CODEC_STATE_H

View File

@ -0,0 +1,35 @@
/*
* Copyright (c) 2024 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef IMAGE_CODEC_TYPE_CONVERTOR_H
#define IMAGE_CODEC_TYPE_CONVERTOR_H
#include <cstdint>
#include "surface_type.h" // foundation/graphic/graphic_2d/interfaces/inner_api/surface/
namespace OHOS::ImagePlugin {
struct PixelFmt {
GraphicPixelFormat graphicFmt; // foundation/graphic/graphic_surface/interfaces/inner_api/surface/surface_type.h
std::string strFmt;
};
class TypeConverter {
public:
// pixel format
static std::optional<PixelFmt> GraphicFmtToFmt(GraphicPixelFormat format);
};
} // namespace OHOS::ImagePlugin
#endif // IMAGE_CODEC_TYPE_CONVERTOR_H

View File

@ -0,0 +1,566 @@
/*
* Copyright (c) 2024 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "hardware/heif_hw_decoder.h"
#include "hardware/imagecodec/image_codec_list.h"
#include "hardware/imagecodec/image_codec_log.h"
#include "hardware/imagecodec/type_converter.h"
#include "media_errors.h" // foundation/multimedia/image_framework/interfaces/innerkits/include/
#include "syspara/parameters.h" // base/startup/init/interfaces/innerkits/include/
#include <fstream>
#include <algorithm>
namespace OHOS::ImagePlugin {
using namespace std;
using namespace HdiCodecNamespace;
bool GridInfo::IsValid() const
{
IF_TRUE_RETURN_VAL_WITH_MSG((displayWidth == 0 || displayHeight == 0), false,
"invalid displaySize: [%{public}ux%{public}u]", displayWidth, displayHeight);
IF_TRUE_RETURN_VAL(!enableGrid, true);
IF_TRUE_RETURN_VAL_WITH_MSG((cols == 0 || rows == 0), false,
"invalid gridSize: [%{public}ux%{public}u]", cols, rows);
IF_TRUE_RETURN_VAL_WITH_MSG((tileWidth == 0 || tileHeight == 0), false,
"invalid tileSize: [%{public}ux%{public}u]", tileWidth, tileHeight);
uint32_t expCols = static_cast<uint32_t>(ceil(static_cast<float>(displayWidth) / static_cast<float>(tileWidth)));
IF_TRUE_RETURN_VAL_WITH_MSG(expCols != cols, false,
"invalid cols, expect %{public}u, get %{public}u", expCols, cols);
uint32_t expRows = static_cast<uint32_t>(ceil(static_cast<float>(displayHeight) / static_cast<float>(tileHeight)));
IF_TRUE_RETURN_VAL_WITH_MSG(expRows != rows, false,
"invalid rows, expect %{public}u, get %{public}u", expRows, rows);
return true;
}
HeifHardwareDecoder::HeifDecoderCallback::HeifDecoderCallback(HeifHardwareDecoder* heifDecoder)
: heifDecoder_(heifDecoder)
{}
void HeifHardwareDecoder::HeifDecoderCallback::OnError(ImageCodecError err)
{
heifDecoder_->SignalError();
}
void HeifHardwareDecoder::HeifDecoderCallback::OnOutputFormatChanged(const Format &format)
{}
void HeifHardwareDecoder::HeifDecoderCallback::OnInputBufferAvailable(uint32_t index,
std::shared_ptr<ImageCodecBuffer> buffer)
{
lock_guard<mutex> lk(heifDecoder_->inputMtx_);
heifDecoder_->inputList_.emplace_back(index, buffer);
heifDecoder_->inputCond_.notify_all();
}
void HeifHardwareDecoder::HeifDecoderCallback::OnOutputBufferAvailable(uint32_t index,
std::shared_ptr<ImageCodecBuffer> buffer)
{
lock_guard<mutex> lk(heifDecoder_->outputMtx_);
heifDecoder_->outputList_.emplace_back(index, buffer);
heifDecoder_->outputCond_.notify_all();
}
HeifHardwareDecoder::HeifHardwareDecoder() : uvOffsetForOutput_(0)
{
heifDecoderImpl_ = ImageCodec::Create();
}
HeifHardwareDecoder::~HeifHardwareDecoder()
{
if (releaseThread_.joinable()) {
releaseThread_.join();
}
Reset();
}
sptr<SurfaceBuffer> HeifHardwareDecoder::AllocateOutputBuffer(uint32_t width, uint32_t height, int32_t pixelFmt)
{
HeifPerfTracker tracker(__FUNCTION__);
LOGI("BufferInfo: width=%{public}u, height=%{public}u, pixelFmt=%{public}d", width, height, pixelFmt);
IF_TRUE_RETURN_VAL_WITH_MSG(heifDecoderImpl_ == nullptr, nullptr, "failed to create heif decoder");
IF_TRUE_RETURN_VAL_WITH_MSG((width == 0 || height == 0), nullptr,
"invalid size=[%{public}ux%{public}u]", width, height);
optional<PixelFmt> fmt = TypeConverter::GraphicFmtToFmt(static_cast<GraphicPixelFormat>(pixelFmt));
IF_TRUE_RETURN_VAL(!fmt.has_value(), nullptr);
uint64_t usage;
int32_t err = heifDecoderImpl_->GetOutputBufferUsage(usage);
IF_TRUE_RETURN_VAL_WITH_MSG(err != IC_ERR_OK, nullptr, "failed to get output buffer usage, err=%{public}d", err);
sptr<SurfaceBuffer> output = SurfaceBuffer::Create();
IF_TRUE_RETURN_VAL_WITH_MSG(output == nullptr, nullptr, "failed to create output");
BufferRequestConfig config = {
.width = width,
.height = height,
.strideAlignment = STRIDE_ALIGNMENT,
.format = pixelFmt,
.usage = usage,
.timeout = 0
};
GSError ret = output->Alloc(config);
IF_TRUE_RETURN_VAL_WITH_MSG(ret != GSERROR_OK, nullptr, "failed to alloc output, ret=%{public}d", ret);
return output;
}
static bool IsValueInRange(uint32_t value, HdiCodecNamespace::RangeValue range)
{
return (value >= static_cast<uint32_t>(range.min)) && (value <= static_cast<uint32_t>(range.max));
}
bool HeifHardwareDecoder::IsHardwareDecodeSupported(const GridInfo& gridInfo)
{
string decoderName = heifDecoderImpl_->GetComponentName();
vector<CodecCompCapability> capList = GetCapList();
auto it = find_if(capList.begin(), capList.end(), [decoderName](const CodecCompCapability& cap) {
return (cap.compName == decoderName);
});
if (it == capList.end()) {
LOGE("can not find heif hw decoder");
return false;
}
uint32_t widthToCheck = gridInfo.enableGrid ? gridInfo.tileWidth : gridInfo.displayWidth;
uint32_t heightToCheck = gridInfo.enableGrid ? gridInfo.tileHeight : gridInfo.displayHeight;
HdiCodecNamespace::RangeValue widthRange = {
.min = it->port.video.minSize.width,
.max = it->port.video.maxSize.width
};
HdiCodecNamespace::RangeValue heightRange = {
.min = it->port.video.minSize.height,
.max = it->port.video.maxSize.height
};
bool isValidSize = false;
if (it->canSwapWidthHeight) {
LOGI("decoder support swap width and height");
isValidSize = (IsValueInRange(widthToCheck, widthRange) && IsValueInRange(heightToCheck, heightRange)) ||
(IsValueInRange(heightToCheck, widthRange) && IsValueInRange(widthToCheck, heightRange));
} else {
isValidSize = IsValueInRange(widthToCheck, widthRange) && IsValueInRange(heightToCheck, heightRange);
}
if (!isValidSize) {
LOGE("unsupported size: [%{public}ux%{public}u]", widthToCheck, heightToCheck);
return false;
}
return true;
}
bool HeifHardwareDecoder::SetCallbackForDecoder()
{
HeifPerfTracker tracker(__FUNCTION__);
shared_ptr<HeifDecoderCallback> cb = make_shared<HeifDecoderCallback>(this);
int32_t ret = heifDecoderImpl_->SetCallback(cb);
if (ret != IC_ERR_OK) {
LOGE("failed to set callback for decoder, err=%{public}d", ret);
return false;
}
return true;
}
bool HeifHardwareDecoder::ConfigureDecoder(const GridInfo& gridInfo, sptr<SurfaceBuffer>& output)
{
HeifPerfTracker tracker(__FUNCTION__);
Format format;
if (gridInfo.enableGrid) {
format.SetValue(ImageCodecDescriptionKey::WIDTH, gridInfo.tileWidth);
format.SetValue(ImageCodecDescriptionKey::HEIGHT, gridInfo.tileHeight);
} else {
format.SetValue(ImageCodecDescriptionKey::WIDTH, gridInfo.displayWidth);
format.SetValue(ImageCodecDescriptionKey::HEIGHT, gridInfo.displayHeight);
}
static constexpr double OUTPUT_FRAME_RATE = 120.0;
format.SetValue(ImageCodecDescriptionKey::FRAME_RATE, OUTPUT_FRAME_RATE);
format.SetValue(ImageCodecDescriptionKey::VIDEO_FRAME_RATE_ADAPTIVE_MODE, true);
int32_t pixelFmt = output->GetFormat();
pixelFmt = (pixelFmt == GRAPHIC_PIXEL_FMT_YCBCR_P010) ? GRAPHIC_PIXEL_FMT_YCBCR_420_SP : pixelFmt;
pixelFmt = (pixelFmt == GRAPHIC_PIXEL_FMT_YCRCB_P010) ? GRAPHIC_PIXEL_FMT_YCRCB_420_SP : pixelFmt;
format.SetValue(ImageCodecDescriptionKey::PIXEL_FORMAT, pixelFmt);
format.SetValue(ImageCodecDescriptionKey::ENABLE_HEIF_GRID, gridInfo.enableGrid);
if (!gridInfo.enableGrid) {
static constexpr uint32_t INPUT_BUFFER_CNT_WHEN_NO_GRID = 3;
format.SetValue(ImageCodecDescriptionKey::INPUT_BUFFER_COUNT, INPUT_BUFFER_CNT_WHEN_NO_GRID);
}
static constexpr uint32_t OUTPUT_BUFFER_CNT = 1;
format.SetValue(ImageCodecDescriptionKey::OUTPUT_BUFFER_COUNT, OUTPUT_BUFFER_CNT);
int32_t ret = heifDecoderImpl_->Configure(format);
if (ret != IC_ERR_OK) {
LOGE("failed to configure decoder, err=%{public}d", ret);
return false;
}
return true;
}
bool HeifHardwareDecoder::SetOutputBuffer(const GridInfo& gridInfo, sptr<SurfaceBuffer> output)
{
HeifPerfTracker tracker(__FUNCTION__);
if (gridInfo.enableGrid) {
return true;
}
int32_t ret = heifDecoderImpl_->SetOutputBuffer(output);
if (ret != IC_ERR_OK) {
LOGE("failed to set output buffer, err=%{public}d", ret);
return false;
}
return true;
}
bool HeifHardwareDecoder::GetUvPlaneOffsetFromSurfaceBuffer(sptr<SurfaceBuffer>& surfaceBuffer, uint64_t& offset)
{
IF_TRUE_RETURN_VAL_WITH_MSG(surfaceBuffer == nullptr, false, "invalid surface buffer");
OH_NativeBuffer_Planes* outputPlanes = nullptr;
GSError ret = surfaceBuffer->GetPlanesInfo(reinterpret_cast<void**>(&outputPlanes));
IF_TRUE_RETURN_VAL_WITH_MSG((ret != GSERROR_OK || outputPlanes == nullptr), false,
"GetPlanesInfo failed, GSError=%{public}d", ret);
IF_TRUE_RETURN_VAL_WITH_MSG(outputPlanes->planeCount < PLANE_BUTT, false,
"invalid yuv buffer, %{public}u", outputPlanes->planeCount);
int32_t pixelFmt = surfaceBuffer->GetFormat();
if (pixelFmt == GRAPHIC_PIXEL_FMT_YCBCR_420_SP || pixelFmt == GRAPHIC_PIXEL_FMT_YCBCR_P010) {
offset = outputPlanes->planes[PLANE_U].offset;
} else {
offset = outputPlanes->planes[PLANE_V].offset;
}
return true;
}
uint32_t HeifHardwareDecoder::DoDecode(const GridInfo& gridInfo, std::vector<std::vector<uint8_t>>& inputs,
sptr<SurfaceBuffer>& output)
{
LOGI("GridInfo: displayWidth=%{public}u, displayHeight=%{public}u, enableGrid=%{public}d, " \
"cols=%{public}u, rows=%{public}u, tileWidth=%{public}u, tileHeight=%{public}u",
gridInfo.displayWidth, gridInfo.displayHeight, gridInfo.enableGrid, gridInfo.cols, gridInfo.rows,
gridInfo.tileWidth, gridInfo.tileHeight);
HeifPerfTracker tracker(__FUNCTION__);
IF_TRUE_RETURN_VAL(!gridInfo.IsValid(), Media::ERR_IMAGE_INVALID_PARAMETER);
IF_TRUE_RETURN_VAL_WITH_MSG(output == nullptr, Media::ERR_IMAGE_INVALID_PARAMETER, "null output");
IF_TRUE_RETURN_VAL_WITH_MSG(inputs.size() < MIN_SIZE_OF_INPUT, Media::ERR_IMAGE_INVALID_PARAMETER,
"input size < %{public}zu", MIN_SIZE_OF_INPUT);
IF_TRUE_RETURN_VAL_WITH_MSG(heifDecoderImpl_ == nullptr, Media::ERR_IMAGE_DECODE_FAILED,
"failed to create heif decoder");
IF_TRUE_RETURN_VAL(!IsHardwareDecodeSupported(gridInfo), Media::ERR_IMAGE_HW_DECODE_UNSUPPORT);
IF_TRUE_RETURN_VAL(!SetCallbackForDecoder(), Media::ERR_IMAGE_DECODE_FAILED);
IF_TRUE_RETURN_VAL(!ConfigureDecoder(gridInfo, output), Media::ERR_IMAGE_DECODE_FAILED);
IF_TRUE_RETURN_VAL(!SetOutputBuffer(gridInfo, output), Media::ERR_IMAGE_DECODE_FAILED);
Reset();
output_ = output;
IF_TRUE_RETURN_VAL(!GetUvPlaneOffsetFromSurfaceBuffer(output_, uvOffsetForOutput_),
Media::ERR_IMAGE_DECODE_FAILED);
gridInfo_ = gridInfo;
thread inputThread(&HeifHardwareDecoder::SendInputBufferLoop, this, inputs);
thread outputThread(&HeifHardwareDecoder::ReceiveOutputBufferLoop, this);
int32_t ret = heifDecoderImpl_->Start();
if (ret != IC_ERR_OK) {
LOGE("failed to start decoder, err=%{public}d", ret);
SignalError();
}
if (inputThread.joinable()) {
inputThread.join();
}
if (outputThread.joinable()) {
outputThread.join();
}
releaseThread_ = thread(&HeifHardwareDecoder::ReleaseDecoder, this);
IF_TRUE_RETURN_VAL_WITH_MSG(hasErr_, Media::ERR_IMAGE_DECODE_FAILED, "err occured during decode");
FlushOutput();
if (OHOS::system::GetBoolParameter("image.codec.dump", false)) {
DumpOutput();
}
return Media::SUCCESS;
}
void HeifHardwareDecoder::ReleaseDecoder()
{
HeifPerfTracker tracker(__FUNCTION__);
int32_t ret = heifDecoderImpl_->Release();
if (ret != IC_ERR_OK) {
LOGE("failed to release decoder, err=%{public}d", ret);
}
}
void HeifHardwareDecoder::Reset()
{
hasErr_ = false;
output_ = nullptr;
inputList_.clear();
outputList_.clear();
}
void HeifHardwareDecoder::FlushOutput()
{
if (output_->GetUsage() & BUFFER_USAGE_MEM_MMZ_CACHE) {
GSError err = output_->Map();
if (err != GSERROR_OK) {
LOGW("Map failed, GSError=%{public}d", err);
return;
}
err = output_->FlushCache();
if (err != GSERROR_OK) {
LOGW("FlushCache failed, GSError=%{public}d", err);
}
}
}
string HeifHardwareDecoder::GetOutputPixelFmtDesc()
{
optional<PixelFmt> fmt = TypeConverter::GraphicFmtToFmt(static_cast<GraphicPixelFormat>(output_->GetFormat()));
if (fmt.has_value()) {
return fmt->strFmt;
}
return "unknown";
}
void HeifHardwareDecoder::DumpOutput()
{
static constexpr char DUMP_PATH[] = "/data/misc/imagecodecdump";
string pixelFmtDesc = GetOutputPixelFmtDesc();
static constexpr int MAX_PATH_LEN = 256;
char outputFilePath[MAX_PATH_LEN] = {0};
int ret = 0;
if (gridInfo_.enableGrid) {
ret = sprintf_s(outputFilePath, sizeof(outputFilePath), "%s/out_%s_%ux%u_grid_%ux%u_%ux%u.bin",
DUMP_PATH, pixelFmtDesc.c_str(), gridInfo_.displayWidth, gridInfo_.displayHeight,
gridInfo_.tileWidth, gridInfo_.tileHeight, gridInfo_.cols, gridInfo_.rows);
} else {
ret = sprintf_s(outputFilePath, sizeof(outputFilePath), "%s/out_%s_%ux%u_nogrid.bin",
DUMP_PATH, pixelFmtDesc.c_str(), gridInfo_.displayWidth, gridInfo_.displayHeight);
}
if (ret == -1) {
LOGE("failed to create dump file");
return;
}
LOGI("dump result to: %{public}s", outputFilePath);
std::ofstream dumpOutFile;
dumpOutFile.open(std::string(outputFilePath), std::ios_base::binary | std::ios_base::trunc);
if (!dumpOutFile.is_open()) {
LOGE("failed to dump decode result");
return;
}
GSError err = output_->InvalidateCache();
if (err != GSERROR_OK) {
LOGW("InvalidateCache failed, GSError=%{public}d", err);
}
dumpOutFile.write(reinterpret_cast<char*>(output_->GetVirAddr()), output_->GetSize());
dumpOutFile.close();
}
int64_t HeifHardwareDecoder::GetTimestampInUs()
{
auto now = chrono::steady_clock::now();
return chrono::duration_cast<chrono::microseconds>(now.time_since_epoch()).count();
}
int32_t HeifHardwareDecoder::PrepareInputCodecBuffer(const vector<vector<uint8_t>>& inputs, size_t inputIndex,
shared_ptr<ImageCodecBuffer>& buffer)
{
HeifPerfTracker tracker(__FUNCTION__);
int64_t pts = GetTimestampInUs();
if (inputIndex >= inputs.size()) {
buffer->SetBufferCirculateInfo(pts, OMX_BUFFERFLAG_EOS, 0, 0);
return 0;
}
const vector<uint8_t>& one = inputs[inputIndex];
if (one.empty()) {
LOGW("inputs[%{public}zu] is empty", inputIndex);
return -1;
}
errno_t ret = memcpy_s(buffer->GetAddr(), static_cast<size_t>(buffer->GetCapacity()), one.data(), one.size());
if (ret != EOK) {
LOGE("failed to get input");
return -1;
}
uint32_t flag = inputIndex == 0 ? OMX_BUFFERFLAG_CODECCONFIG : 0;
int32_t size = static_cast<int32_t>(one.size());
buffer->SetBufferCirculateInfo(pts, flag, static_cast<uint32_t>(size), 0);
return size;
}
bool HeifHardwareDecoder::WaitForOmxToReturnInputBuffer(uint32_t& bufferId, shared_ptr<ImageCodecBuffer>& buffer)
{
unique_lock<mutex> lk(inputMtx_);
bool ret = inputCond_.wait_for(lk, chrono::milliseconds(BUFFER_CIRCULATE_TIMEOUT_IN_MS), [this] {
return !inputList_.empty();
});
if (!ret) {
return false;
}
std::tie(bufferId, buffer) = inputList_.front();
inputList_.pop_front();
return true;
}
void HeifHardwareDecoder::SendInputBufferLoop(const vector<vector<uint8_t>>& inputs)
{
LOGI("in");
size_t inputIndex = 0;
bool eos = false;
while (!eos && !HasError()) {
uint32_t bufferId;
shared_ptr<ImageCodecBuffer> buffer;
if (!WaitForOmxToReturnInputBuffer(bufferId, buffer)) {
LOGE("input time out");
continue;
}
if (buffer == nullptr) {
LOGE("got null input buffer");
break;
}
int32_t size = PrepareInputCodecBuffer(inputs, inputIndex, buffer);
if (size >= 0) {
int32_t ret = heifDecoderImpl_->QueueInputBuffer(bufferId);
if (ret != IC_ERR_OK) {
LOGE("failed to queue input buffer");
}
}
++inputIndex;
eos = (size == 0);
}
LOGI("out");
}
bool HeifHardwareDecoder::WaitForOmxToReturnOutputBuffer(uint32_t& bufferId, shared_ptr<ImageCodecBuffer>& buffer)
{
unique_lock<mutex> lk(outputMtx_);
bool ret = outputCond_.wait_for(lk, chrono::milliseconds(BUFFER_CIRCULATE_TIMEOUT_IN_MS), [this] {
return !outputList_.empty();
});
if (!ret) {
return false;
}
std::tie(bufferId, buffer) = outputList_.front();
outputList_.pop_front();
return true;
}
bool HeifHardwareDecoder::CopyRawYuvData(const RawYuvCopyInfo& src, const RawYuvCopyInfo& dst,
uint32_t dirtyWidth, uint32_t dirtyHeight)
{
IF_TRUE_RETURN_VAL_WITH_MSG((dst.yStart == nullptr || dst.uvStart == nullptr),
false, "can not get addr from dst buffer");
IF_TRUE_RETURN_VAL_WITH_MSG((src.yStart == nullptr || src.uvStart == nullptr),
false, "can not get addr from src buffer");
errno_t ret = EOK;
// copy Y plane
for (uint32_t row = 0; (row < dirtyHeight) && (ret == EOK); ++row) {
ret = memcpy_s(dst.yStart + dst.yOffset + row * dst.stride, static_cast<size_t>(dirtyWidth),
src.yStart + src.yOffset + row * src.stride, static_cast<size_t>(dirtyWidth));
}
// copy UV plane
uint32_t dirtyHeightForUvPlane = (dirtyHeight + SAMPLE_RATIO_FOR_YUV420_SP - 1) / SAMPLE_RATIO_FOR_YUV420_SP;
for (uint32_t row = 0; (row < dirtyHeightForUvPlane) && (ret == EOK); ++row) {
ret = memcpy_s(dst.uvStart + dst.uvOffset + row * dst.stride, static_cast<size_t>(dirtyWidth),
src.uvStart + src.uvOffset + row * src.stride, static_cast<size_t>(dirtyWidth));
}
if (ret != EOK) {
LOGE("failed to copy grid data, err=%{public}d", ret);
return false;
}
return true;
}
uint32_t HeifHardwareDecoder::CalculateDirtyLen(uint32_t displayLen, uint32_t gridLen,
uint32_t totalGrid, uint32_t curGrid)
{
uint32_t dirtyLen = 0;
if (gridLen >= displayLen) {
dirtyLen = displayLen;
} else {
dirtyLen = gridLen;
if (curGrid + 1 == totalGrid) {
dirtyLen = displayLen - curGrid * gridLen;
}
}
return dirtyLen;
}
void HeifHardwareDecoder::AssembleOutput(uint32_t outputIndex, shared_ptr<ImageCodecBuffer>& buffer)
{
HeifPerfTracker tracker(__FUNCTION__);
uint64_t srcUvOffset = 0;
sptr<SurfaceBuffer> srcSurfaceBuffer = buffer->GetSurfaceBuffer();
if (!GetUvPlaneOffsetFromSurfaceBuffer(srcSurfaceBuffer, srcUvOffset)) {
SignalError();
return;
}
RawYuvCopyInfo dst;
dst.yStart = static_cast<uint8_t*>(output_->GetVirAddr());
dst.stride = static_cast<uint32_t>(output_->GetStride());
dst.uvStart = dst.yStart + uvOffsetForOutput_;
dst.yStride = static_cast<uint32_t>(uvOffsetForOutput_ / static_cast<uint64_t>(dst.stride));
RawYuvCopyInfo src;
src.yStart = buffer->GetAddr();
src.stride = static_cast<uint32_t>(buffer->GetStride());
src.uvStart = src.yStart + srcUvOffset;
src.yStride = static_cast<uint32_t>(srcUvOffset / static_cast<uint64_t>(src.stride));
src.yOffset = 0;
src.uvOffset = 0;
uint32_t decodedRows = outputIndex / gridInfo_.cols;
uint32_t decodedCols = outputIndex % gridInfo_.cols;
uint32_t dirtyWidth = CalculateDirtyLen(dst.stride, src.stride, gridInfo_.cols, decodedCols);
uint32_t dirtyHeight = CalculateDirtyLen(dst.yStride, src.yStride, gridInfo_.rows, decodedRows);
dst.yOffset = decodedRows * dst.stride * gridInfo_.tileHeight + decodedCols * src.stride;
dst.uvOffset = decodedRows * dst.stride * gridInfo_.tileHeight / SAMPLE_RATIO_FOR_YUV420_SP +
decodedCols * src.stride;
if (!CopyRawYuvData(src, dst, dirtyWidth, dirtyHeight)) {
LOGE("failed to assemble output(grid=%{public}d))", outputIndex);
SignalError();
}
}
void HeifHardwareDecoder::ReceiveOutputBufferLoop()
{
LOGI("in");
uint32_t outputIndex = 0;
while (!HasError()) {
uint32_t bufferId;
shared_ptr<ImageCodecBuffer> buffer;
if (!WaitForOmxToReturnOutputBuffer(bufferId, buffer)) {
LOGE("output time out");
continue;
}
if (buffer == nullptr) {
LOGE("null output buffer");
break;
}
uint32_t flag = buffer->GetBufferFlag();
if (flag & OMX_BUFFERFLAG_EOS) {
LOGI("output eos, quit loop");
break;
}
if (gridInfo_.enableGrid) {
AssembleOutput(outputIndex, buffer);
}
++outputIndex;
int32_t ret = heifDecoderImpl_->ReleaseOutputBuffer(bufferId);
if (ret != IC_ERR_OK) {
LOGE("failed to release output buffer");
}
}
LOGI("out");
}
void HeifHardwareDecoder::SignalError()
{
std::lock_guard<std::mutex> lk(errMtx_);
hasErr_ = true;
}
bool HeifHardwareDecoder::HasError()
{
std::lock_guard<std::mutex> lk(errMtx_);
return hasErr_;
}
} // namespace OHOS::ImagePlugin

View File

@ -0,0 +1,710 @@
/*
* Copyright (c) 2024 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "hardware/imagecodec/image_codec.h"
#include "hardware/imagecodec/image_codec_list.h"
#include "hardware/imagecodec/image_codec_log.h"
namespace OHOS::ImagePlugin {
using namespace std;
using namespace HdiCodecNamespace;
/**************************** BaseState Start ****************************/
void ImageCodec::BaseState::OnMsgReceived(const MsgInfo &info)
{
switch (info.type) {
case MsgWhat::CODEC_EVENT: {
OnCodecEvent(info);
return;
}
case MsgWhat::OMX_EMPTY_BUFFER_DONE: {
uint32_t bufferId;
(void)info.param->GetValue(BUFFER_ID, bufferId);
codec_->OnOMXEmptyBufferDone(bufferId, inputMode_);
return;
}
case MsgWhat::OMX_FILL_BUFFER_DONE: {
OmxCodecBuffer omxBuffer;
(void)info.param->GetValue("omxBuffer", omxBuffer);
codec_->OnOMXFillBufferDone(omxBuffer, outputMode_);
return;
}
case MsgWhat::GET_INPUT_FORMAT:
case MsgWhat::GET_OUTPUT_FORMAT: {
OnGetFormat(info);
return;
}
case MsgWhat::RELEASE: {
OnShutDown(info);
return;
}
default: {
const char* msgWhat = ImageCodec::ToString(static_cast<MsgWhat>(info.type));
if (info.id == ASYNC_MSG_ID) {
SLOGI("ignore msg %{public}s in current state", msgWhat);
} else { // Make sure that all sync message are replied
SLOGE("%{public}s cannot be called at this state", msgWhat);
ReplyErrorCode(info.id, IC_ERR_INVALID_STATE);
}
return;
}
}
}
void ImageCodec::BaseState::ReplyErrorCode(MsgId id, int32_t err)
{
if (id == ASYNC_MSG_ID) {
return;
}
ParamSP reply = ParamBundle::Create();
reply->SetValue("err", err);
codec_->PostReply(id, reply);
}
void ImageCodec::BaseState::OnCodecEvent(const MsgInfo &info)
{
CodecEventType event;
uint32_t data1;
uint32_t data2;
(void)info.param->GetValue("event", event);
(void)info.param->GetValue("data1", data1);
(void)info.param->GetValue("data2", data2);
if (event == CODEC_EVENT_CMD_COMPLETE &&
data1 == static_cast<uint32_t>(CODEC_COMMAND_FLUSH) &&
data2 == static_cast<uint32_t>(OMX_ALL)) {
SLOGD("ignore flush all complete event");
} else {
OnCodecEvent(event, data1, data2);
}
}
void ImageCodec::BaseState::OnCodecEvent(CodecEventType event, uint32_t data1, uint32_t data2)
{
if (event == CODEC_EVENT_ERROR) {
SLOGE("omx report error event, data1 = %{public}u, data2 = %{public}u", data1, data2);
codec_->SignalError(IC_ERR_SERVICE_DIED);
} else {
SLOGW("ignore event %{public}d, data1 = %{public}u, data2 = %{public}u", event, data1, data2);
}
}
void ImageCodec::BaseState::OnGetFormat(const MsgInfo &info)
{
shared_ptr<Format> fmt = (info.type == MsgWhat::GET_INPUT_FORMAT) ?
codec_->inputFormat_ : codec_->outputFormat_;
ParamSP reply = ParamBundle::Create();
if (fmt) {
reply->SetValue<int32_t>("err", IC_ERR_OK);
reply->SetValue("format", *fmt);
codec_->PostReply(info.id, reply);
} else {
ReplyErrorCode(info.id, IC_ERR_UNKNOWN);
}
}
void ImageCodec::BaseState::OnCheckIfStuck(const MsgInfo &info)
{
int32_t generation;
(void)info.param->GetValue("generation", generation);
if (generation == codec_->stateGeneration_) {
SLOGE("stucked");
codec_->PrintAllBufferInfo();
codec_->SignalError(IC_ERR_UNKNOWN);
}
}
void ImageCodec::BaseState::OnForceShutDown(const MsgInfo &info)
{
int32_t generation;
(void)info.param->GetValue("generation", generation);
codec_->ForceShutdown(generation);
}
/**************************** BaseState End ******************************/
/**************************** UninitializedState start ****************************/
void ImageCodec::UninitializedState::OnStateEntered()
{
codec_->ReleaseComponent();
}
void ImageCodec::UninitializedState::OnMsgReceived(const MsgInfo &info)
{
switch (info.type) {
case MsgWhat::INIT: {
string name;
(void)info.param->GetValue("name", name);
int32_t err = OnAllocateComponent(name);
ReplyErrorCode(info.id, err);
if (err == IC_ERR_OK) {
codec_->ChangeStateTo(codec_->initializedState_);
}
break;
}
default: {
BaseState::OnMsgReceived(info);
}
}
}
int32_t ImageCodec::UninitializedState::OnAllocateComponent(const std::string &name)
{
codec_->compMgr_ = GetManager();
if (codec_->compMgr_ == nullptr) {
SLOGE("GetCodecComponentManager failed");
return IC_ERR_UNKNOWN;
}
codec_->compCb_ = new HdiCallback(codec_);
int32_t ret = codec_->compMgr_->CreateComponent(codec_->compNode_, codec_->componentId_, name,
0, codec_->compCb_);
if (ret != HDF_SUCCESS || codec_->compNode_ == nullptr) {
codec_->compCb_ = nullptr;
codec_->compMgr_ = nullptr;
SLOGE("CreateComponent failed, ret=%{public}d", ret);
return IC_ERR_UNKNOWN;
}
codec_->componentName_ = name;
codec_->compUniqueStr_ = "[" + to_string(codec_->componentId_) + "][" + name + "]";
SLOGI("create omx node succ");
return IC_ERR_OK;
}
void ImageCodec::UninitializedState::OnShutDown(const MsgInfo &info)
{
ReplyErrorCode(info.id, IC_ERR_OK);
}
/**************************** UninitializedState End ******************************/
/**************************** InitializedState Start **********************************/
void ImageCodec::InitializedState::OnStateEntered()
{
codec_->inputPortEos_ = false;
codec_->outputPortEos_ = false;
codec_->outputFormat_.reset();
ProcessShutDownFromRunning();
codec_->notifyCallerAfterShutdownComplete_ = false;
codec_->ProcessDeferredMessages();
}
void ImageCodec::InitializedState::ProcessShutDownFromRunning()
{
if (!codec_->isShutDownFromRunning_) {
return;
}
SLOGI("we are doing shutdown from running/portchange/flush -> stopping -> initialized");
codec_->ChangeStateTo(codec_->uninitializedState_);
if (codec_->notifyCallerAfterShutdownComplete_) {
SLOGI("reply to release msg");
MsgInfo msg { MsgWhat::RELEASE, 0, nullptr };
if (codec_->GetFirstSyncMsgToReply(msg)) {
ReplyErrorCode(msg.id, IC_ERR_OK);
}
codec_->notifyCallerAfterShutdownComplete_ = false;
}
codec_->isShutDownFromRunning_ = false;
}
void ImageCodec::InitializedState::OnMsgReceived(const MsgInfo &info)
{
switch (info.type) {
case MsgWhat::SET_CALLBACK: {
OnSetCallBack(info);
return;
}
case MsgWhat::CONFIGURE: {
OnConfigure(info);
return;
}
case MsgWhat::GET_OUTPUT_BUFFER_USAGE: {
OnGetOutputBufferUsage(info);
break;
}
case MsgWhat::SET_OUTPUT_BUFFER: {
OnSetOutputBuffer(info);
break;
}
case MsgWhat::START: {
OnStart(info);
return;
}
default: {
BaseState::OnMsgReceived(info);
}
}
}
void ImageCodec::InitializedState::OnSetCallBack(const MsgInfo &info)
{
int32_t err;
shared_ptr<ImageCodecCallback> cb;
(void)info.param->GetValue("callback", cb);
if (cb == nullptr) {
err = IC_ERR_INVALID_VAL;
SLOGE("invalid param");
} else {
codec_->callback_ = cb;
err = IC_ERR_OK;
}
ReplyErrorCode(info.id, err);
}
void ImageCodec::InitializedState::OnConfigure(const MsgInfo &info)
{
Format fmt;
(void)info.param->GetValue("format", fmt);
ReplyErrorCode(info.id, codec_->OnConfigure(fmt));
}
void ImageCodec::InitializedState::OnGetOutputBufferUsage(const MsgInfo &info)
{
ParamSP reply = ParamBundle::Create();
reply->SetValue<int32_t>("err", IC_ERR_OK);
reply->SetValue("usage", codec_->OnGetOutputBufferUsage());
codec_->PostReply(info.id, reply);
}
void ImageCodec::InitializedState::OnSetOutputBuffer(const MsgInfo &info)
{
sptr<SurfaceBuffer> output;
(void)info.param->GetValue("output", output);
ReplyErrorCode(info.id, codec_->OnSetOutputBuffer(output));
}
void ImageCodec::InitializedState::OnStart(const MsgInfo &info)
{
if (!codec_->ReadyToStart()) {
ReplyErrorCode(info.id, IC_ERR_INVALID_OPERATION);
return;
}
SLOGI("begin to set omx to idle");
int32_t ret = codec_->compNode_->SendCommand(CODEC_COMMAND_STATE_SET, CODEC_STATE_IDLE, {});
if (ret == HDF_SUCCESS) {
codec_->ReplyToSyncMsgLater(info);
codec_->ChangeStateTo(codec_->startingState_);
} else {
SLOGE("set omx to idle failed, ret=%{public}d", ret);
ReplyErrorCode(info.id, IC_ERR_UNKNOWN);
}
}
void ImageCodec::InitializedState::OnShutDown(const MsgInfo &info)
{
SLOGI("receive RELEASE");
codec_->ChangeStateTo(codec_->uninitializedState_);
codec_->notifyCallerAfterShutdownComplete_ = false;
ReplyErrorCode(info.id, IC_ERR_OK);
}
/**************************** InitializedState End ******************************/
/**************************** StartingState Start ******************************/
void ImageCodec::StartingState::OnStateEntered()
{
hasError_ = false;
ParamSP msg = ParamBundle::Create();
msg->SetValue("generation", codec_->stateGeneration_);
codec_->SendAsyncMsg(MsgWhat::CHECK_IF_STUCK, msg, THREE_SECONDS_IN_US);
int32_t ret = AllocateBuffers();
if (ret != IC_ERR_OK) {
SLOGE("AllocateBuffers failed, back to init state");
hasError_ = true;
ReplyStartMsg(ret);
codec_->ChangeStateTo(codec_->initializedState_);
}
}
int32_t ImageCodec::StartingState::AllocateBuffers()
{
int32_t ret = codec_->AllocateBuffersOnPort(OMX_DirInput);
if (ret != IC_ERR_OK) {
return ret;
}
ret = codec_->AllocateBuffersOnPort(OMX_DirOutput);
if (ret != IC_ERR_OK) {
return ret;
}
return IC_ERR_OK;
}
void ImageCodec::StartingState::OnMsgReceived(const MsgInfo &info)
{
switch (info.type) {
case MsgWhat::GET_INPUT_FORMAT:
case MsgWhat::GET_OUTPUT_FORMAT: {
codec_->DeferMessage(info);
return;
}
case MsgWhat::START: {
ReplyErrorCode(info.id, IC_ERR_OK);
return;
}
case MsgWhat::CHECK_IF_STUCK: {
int32_t generation;
if (info.param->GetValue("generation", generation) &&
generation == codec_->stateGeneration_) {
SLOGE("stucked, force state transition");
hasError_ = true;
ReplyStartMsg(IC_ERR_UNKNOWN);
codec_->ChangeStateTo(codec_->initializedState_);
}
return;
}
default: {
BaseState::OnMsgReceived(info);
}
}
}
void ImageCodec::StartingState::OnCodecEvent(CodecEventType event, uint32_t data1, uint32_t data2)
{
if (event != CODEC_EVENT_CMD_COMPLETE) {
return BaseState::OnCodecEvent(event, data1, data2);
}
if (data1 != static_cast<uint32_t>(CODEC_COMMAND_STATE_SET)) {
SLOGW("ignore event: data1=%{public}u, data2=%{public}u", data1, data2);
return;
}
if (data2 == static_cast<uint32_t>(CODEC_STATE_IDLE)) {
SLOGI("omx now idle, begin to set omx to executing");
int32_t ret = codec_->compNode_->SendCommand(CODEC_COMMAND_STATE_SET, CODEC_STATE_EXECUTING, {});
if (ret != HDF_SUCCESS) {
SLOGE("set omx to executing failed, ret=%{public}d", ret);
hasError_ = true;
ReplyStartMsg(IC_ERR_UNKNOWN);
codec_->ChangeStateTo(codec_->initializedState_);
}
} else if (data2 == static_cast<uint32_t>(CODEC_STATE_EXECUTING)) {
SLOGI("omx now executing");
ReplyStartMsg(IC_ERR_OK);
codec_->SubmitAllBuffersOwnedByUs();
codec_->ChangeStateTo(codec_->runningState_);
}
}
void ImageCodec::StartingState::OnShutDown(const MsgInfo &info)
{
codec_->DeferMessage(info);
}
void ImageCodec::StartingState::ReplyStartMsg(int32_t errCode)
{
MsgInfo msg {MsgWhat::START, 0, nullptr};
if (codec_->GetFirstSyncMsgToReply(msg)) {
SLOGI("start %{public}s", (errCode == 0) ? "succ" : "failed");
ReplyErrorCode(msg.id, errCode);
} else {
SLOGE("there should be a start msg to reply");
}
}
void ImageCodec::StartingState::OnStateExited()
{
if (hasError_) {
SLOGW("error occured, roll omx back to loaded and free allocated buffers");
if (codec_->RollOmxBackToLoaded()) {
codec_->ClearBufferPool(OMX_DirInput);
codec_->ClearBufferPool(OMX_DirOutput);
}
}
BaseState::OnStateExited();
}
/**************************** StartingState End ******************************/
/**************************** RunningState Start ********************************/
void ImageCodec::RunningState::OnStateEntered()
{
codec_->ProcessDeferredMessages();
}
void ImageCodec::RunningState::OnMsgReceived(const MsgInfo &info)
{
switch (info.type) {
case MsgWhat::START:
ReplyErrorCode(info.id, codec_->SubmitAllBuffersOwnedByUs());
break;
case MsgWhat::QUEUE_INPUT_BUFFER:
codec_->OnQueueInputBuffer(info, inputMode_);
break;
case MsgWhat::RELEASE_OUTPUT_BUFFER:
codec_->OnReleaseOutputBuffer(info, outputMode_);
break;
default:
BaseState::OnMsgReceived(info);
break;
}
}
void ImageCodec::RunningState::OnCodecEvent(CodecEventType event, uint32_t data1, uint32_t data2)
{
switch (event) {
case CODEC_EVENT_PORT_SETTINGS_CHANGED: {
if (data1 != OMX_DirOutput) {
SLOGI("ignore input port changed");
return;
}
if (data2 == 0 || data2 == OMX_IndexParamPortDefinition) {
SLOGI("output port settings changed, begin to ask omx to disable out port");
codec_->UpdateOutPortFormat();
int32_t ret = codec_->compNode_->SendCommand(
CODEC_COMMAND_PORT_DISABLE, OMX_DirOutput, {});
if (ret == HDF_SUCCESS) {
codec_->EraseOutBuffersOwnedByUs();
codec_->ChangeStateTo(codec_->outputPortChangedState_);
} else {
SLOGE("ask omx to disable out port failed");
codec_->SignalError(IC_ERR_UNKNOWN);
}
} else if (data2 == OMX_IndexColorAspects) {
codec_->UpdateColorAspects();
} else {
SLOGI("unknown data2 0x%{public}x for CODEC_EVENT_PORT_SETTINGS_CHANGED", data2);
}
return;
}
default: {
BaseState::OnCodecEvent(event, data1, data2);
}
}
}
void ImageCodec::RunningState::OnShutDown(const MsgInfo &info)
{
codec_->isShutDownFromRunning_ = true;
codec_->notifyCallerAfterShutdownComplete_ = true;
codec_->isBufferCirculating_ = false;
SLOGI("receive release msg, begin to set omx to idle");
int32_t ret = codec_->compNode_->SendCommand(CODEC_COMMAND_STATE_SET, CODEC_STATE_IDLE, {});
if (ret == HDF_SUCCESS) {
codec_->ReplyToSyncMsgLater(info);
codec_->ChangeStateTo(codec_->stoppingState_);
} else {
SLOGE("set omx to idle failed, ret=%{public}d", ret);
ReplyErrorCode(info.id, IC_ERR_UNKNOWN);
}
}
/**************************** RunningState End ********************************/
/**************************** OutputPortChangedState Start ********************************/
void ImageCodec::OutputPortChangedState::OnStateEntered()
{
ParamSP msg = ParamBundle::Create();
msg->SetValue("generation", codec_->stateGeneration_);
codec_->SendAsyncMsg(MsgWhat::CHECK_IF_STUCK, msg, THREE_SECONDS_IN_US);
}
void ImageCodec::OutputPortChangedState::OnMsgReceived(const MsgInfo &info)
{
switch (info.type) {
case MsgWhat::START:
case MsgWhat::GET_INPUT_FORMAT:
case MsgWhat::GET_OUTPUT_FORMAT: {
codec_->DeferMessage(info);
return;
}
case MsgWhat::QUEUE_INPUT_BUFFER: {
codec_->OnQueueInputBuffer(info, inputMode_);
return;
}
case MsgWhat::RELEASE_OUTPUT_BUFFER: {
codec_->OnReleaseOutputBuffer(info, outputMode_);
return;
}
case MsgWhat::FORCE_SHUTDOWN: {
OnForceShutDown(info);
return;
}
case MsgWhat::CHECK_IF_STUCK: {
OnCheckIfStuck(info);
return;
}
default: {
BaseState::OnMsgReceived(info);
}
}
}
void ImageCodec::OutputPortChangedState::OnShutDown(const MsgInfo &info)
{
if (codec_->hasFatalError_) {
ParamSP stopMsg = ParamBundle::Create();
stopMsg->SetValue("generation", codec_->stateGeneration_);
codec_->SendAsyncMsg(MsgWhat::FORCE_SHUTDOWN, stopMsg, THREE_SECONDS_IN_US);
}
codec_->ReclaimBuffer(OMX_DirOutput, BufferOwner::OWNED_BY_USER, true);
codec_->DeferMessage(info);
}
void ImageCodec::OutputPortChangedState::OnCodecEvent(CodecEventType event, uint32_t data1, uint32_t data2)
{
switch (event) {
case CODEC_EVENT_CMD_COMPLETE: {
if (data1 == CODEC_COMMAND_PORT_DISABLE) {
if (data2 != OMX_DirOutput) {
SLOGW("ignore input port disable complete");
return;
}
SLOGI("output port is disabled");
HandleOutputPortDisabled();
} else if (data1 == CODEC_COMMAND_PORT_ENABLE) {
if (data2 != OMX_DirOutput) {
SLOGW("ignore input port enable complete");
return;
}
SLOGI("output port is enabled");
HandleOutputPortEnabled();
}
return;
}
default: {
BaseState::OnCodecEvent(event, data1, data2);
}
}
}
void ImageCodec::OutputPortChangedState::HandleOutputPortDisabled()
{
int32_t ret = IC_ERR_OK;
if (!codec_->outputBufferPool_.empty()) {
SLOGE("output port is disabled but not empty: %{public}zu", codec_->outputBufferPool_.size());
ret = IC_ERR_UNKNOWN;
}
if (ret == IC_ERR_OK) {
ret = codec_->ReConfigureOutputBufferCnt();
}
if (ret == IC_ERR_OK) {
SLOGI("begin to ask omx to enable out port");
int32_t err = codec_->compNode_->SendCommand(CODEC_COMMAND_PORT_ENABLE, OMX_DirOutput, {});
if (err == HDF_SUCCESS) {
ret = codec_->AllocateBuffersOnPort(OMX_DirOutput);
} else {
SLOGE("ask omx to enable out port failed, ret=%{public}d", ret);
ret = IC_ERR_UNKNOWN;
}
}
if (ret != IC_ERR_OK) {
codec_->SignalError(IC_ERR_INVALID_VAL);
}
}
void ImageCodec::OutputPortChangedState::HandleOutputPortEnabled()
{
if (codec_->isBufferCirculating_) {
codec_->SubmitOutputBuffersToOmxNode();
}
codec_->callback_->OnOutputFormatChanged(*(codec_->outputFormat_.get()));
codec_->ChangeStateTo(codec_->runningState_);
}
/**************************** OutputPortChangedState End ********************************/
/**************************** StoppingState Start ********************************/
void ImageCodec::StoppingState::OnStateEntered()
{
omxNodeInIdleState_ = false;
omxNodeIsChangingToLoadedState_ = false;
codec_->ReclaimBuffer(OMX_DirInput, BufferOwner::OWNED_BY_USER);
codec_->ReclaimBuffer(OMX_DirOutput, BufferOwner::OWNED_BY_USER);
SLOGI("all buffer owned by user are now owned by us");
ParamSP msg = ParamBundle::Create();
msg->SetValue("generation", codec_->stateGeneration_);
codec_->SendAsyncMsg(MsgWhat::CHECK_IF_STUCK, msg, THREE_SECONDS_IN_US);
}
void ImageCodec::StoppingState::OnMsgReceived(const MsgInfo &info)
{
switch (info.type) {
case MsgWhat::CHECK_IF_STUCK: {
int32_t generation;
(void)info.param->GetValue("generation", generation);
if (generation == codec_->stateGeneration_) {
SLOGE("stucked, force state transition");
codec_->ReclaimBuffer(OMX_DirInput, BufferOwner::OWNED_BY_OMX);
codec_->ReclaimBuffer(OMX_DirOutput, BufferOwner::OWNED_BY_OMX);
SLOGI("all buffer owned by omx are now owned by us");
ChangeOmxNodeToLoadedState(true);
codec_->ChangeStateTo(codec_->initializedState_);
}
return;
}
default: {
BaseState::OnMsgReceived(info);
}
}
}
void ImageCodec::StoppingState::OnCodecEvent(CodecEventType event, uint32_t data1, uint32_t data2)
{
switch (event) {
case CODEC_EVENT_CMD_COMPLETE: {
if (data1 != static_cast<uint32_t>(CODEC_COMMAND_STATE_SET)) {
SLOGW("unexpected CODEC_EVENT_CMD_COMPLETE: %{public}u %{public}u", data1, data2);
return;
}
if (data2 == static_cast<uint32_t>(CODEC_STATE_IDLE)) {
SLOGI("omx now idle");
omxNodeInIdleState_ = true;
ChangeStateIfWeOwnAllBuffers();
} else if (data2 == static_cast<uint32_t>(CODEC_STATE_LOADED)) {
SLOGI("omx now loaded");
codec_->ChangeStateTo(codec_->initializedState_);
}
return;
}
default: {
BaseState::OnCodecEvent(event, data1, data2);
}
}
}
void ImageCodec::StoppingState::ChangeStateIfWeOwnAllBuffers()
{
if (omxNodeInIdleState_ && codec_->IsAllBufferOwnedByUs()) {
ChangeOmxNodeToLoadedState(false);
} else {
SLOGD("cannot change state yet");
}
}
void ImageCodec::StoppingState::ChangeOmxNodeToLoadedState(bool forceToFreeBuffer)
{
if (!omxNodeIsChangingToLoadedState_) {
SLOGI("begin to set omx to loaded");
int32_t ret = codec_->compNode_->SendCommand(CODEC_COMMAND_STATE_SET, CODEC_STATE_LOADED, {});
if (ret == HDF_SUCCESS) {
omxNodeIsChangingToLoadedState_ = true;
} else {
SLOGE("set omx to loaded failed, ret=%{public}d", ret);
}
}
if (forceToFreeBuffer || omxNodeIsChangingToLoadedState_) {
codec_->ClearBufferPool(OMX_DirInput);
codec_->ClearBufferPool(OMX_DirOutput);
return;
}
codec_->SignalError(IC_ERR_UNKNOWN);
}
void ImageCodec::StoppingState::OnShutDown(const MsgInfo &info)
{
codec_->DeferMessage(info);
}
/**************************** StoppingState End ********************************/
} // namespace OHOS::ImagePlugin

View File

@ -0,0 +1,37 @@
/*
* Copyright (c) 2024 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "hardware/imagecodec/format.h"
#include <cinttypes>
namespace OHOS::ImagePlugin {
using namespace std;
Format::Format(const Format& src)
{
m_items = src.m_items;
}
Format& Format::operator=(const Format& src)
{
m_items = src.m_items;
return *this;
}
bool Format::ContainKey(const std::string &key) const
{
return (m_items.find(key) != m_items.end());
}
} // namespace OHOS::ImagePlugin

View File

@ -0,0 +1,988 @@
/*
* Copyright (c) 2024 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "hardware/imagecodec/image_codec.h"
#include "hardware/imagecodec/image_decoder.h"
#include "hardware/imagecodec/image_codec_list.h"
#include "hardware/imagecodec/image_codec_log.h"
#include "syspara/parameters.h" // base/startup/init/interfaces/innerkits/include/
#include "qos.h"
namespace OHOS::ImagePlugin {
using namespace std;
using namespace HdiCodecNamespace;
static bool IsSecureMode(const string &name)
{
string prefix = ".secure";
if (name.length() <= prefix.length()) {
return false;
}
return (name.rfind(prefix) == (name.length() - prefix.length()));
}
shared_ptr<ImageCodec> ImageCodec::Create()
{
vector<CodecCompCapability> capList = GetCapList();
shared_ptr<ImageCodec> codec;
string name;
for (const auto& cap : capList) {
if (cap.role == MEDIA_ROLETYPE_VIDEO_HEVC && cap.type == VIDEO_DECODER && !IsSecureMode(cap.compName)) {
name = cap.compName;
codec = make_shared<ImageDecoder>();
break;
}
}
if ((codec != nullptr) && (codec->InitWithName(name) == IC_ERR_OK)) {
return codec;
}
return nullptr;
}
int32_t ImageCodec::SetCallback(const shared_ptr<ImageCodecCallback> &callback)
{
HLOGI(">>");
function<void(ParamSP)> proc = [&](ParamSP msg) {
msg->SetValue("callback", callback);
};
return DoSyncCall(MsgWhat::SET_CALLBACK, proc);
}
int32_t ImageCodec::Configure(const Format &format)
{
function<void(ParamSP)> proc = [&](ParamSP msg) {
msg->SetValue("format", format);
};
return DoSyncCall(MsgWhat::CONFIGURE, proc);
}
int32_t ImageCodec::QueueInputBuffer(uint32_t index)
{
function<void(ParamSP)> proc = [&](ParamSP msg) {
msg->SetValue(BUFFER_ID, index);
};
return DoSyncCall(MsgWhat::QUEUE_INPUT_BUFFER, proc);
}
int32_t ImageCodec::ReleaseOutputBuffer(uint32_t index)
{
function<void(ParamSP)> proc = [&](ParamSP msg) {
msg->SetValue(BUFFER_ID, index);
};
return DoSyncCall(MsgWhat::RELEASE_OUTPUT_BUFFER, proc);
}
int32_t ImageCodec::GetInputFormat(Format& format)
{
HLOGI(">>");
ParamSP reply;
int32_t ret = DoSyncCallAndGetReply(MsgWhat::GET_INPUT_FORMAT, nullptr, reply);
if (ret != IC_ERR_OK) {
HLOGE("failed to get input format");
return ret;
}
IF_TRUE_RETURN_VAL_WITH_MSG(!reply->GetValue("format", format),
IC_ERR_UNKNOWN, "input format not replied");
return IC_ERR_OK;
}
int32_t ImageCodec::GetOutputFormat(Format& format)
{
ParamSP reply;
int32_t ret = DoSyncCallAndGetReply(MsgWhat::GET_OUTPUT_FORMAT, nullptr, reply);
if (ret != IC_ERR_OK) {
HLOGE("failed to get output format");
return ret;
}
IF_TRUE_RETURN_VAL_WITH_MSG(!reply->GetValue("format", format),
IC_ERR_UNKNOWN, "output format not replied");
return IC_ERR_OK;
}
int32_t ImageCodec::Start()
{
HLOGI(">>");
return DoSyncCall(MsgWhat::START, nullptr);
}
int32_t ImageCodec::Release()
{
HLOGI(">>");
return DoSyncCall(MsgWhat::RELEASE, nullptr);
}
int32_t ImageCodec::GetOutputBufferUsage(uint64_t& usage)
{
ParamSP reply;
int32_t ret = DoSyncCallAndGetReply(MsgWhat::GET_OUTPUT_BUFFER_USAGE, nullptr, reply);
if (ret != IC_ERR_OK) {
HLOGE("failed to get output buffer usage");
return ret;
}
IF_TRUE_RETURN_VAL_WITH_MSG(!reply->GetValue("usage", usage),
IC_ERR_UNKNOWN, "output buffer usage not replied");
return IC_ERR_OK;
}
int32_t ImageCodec::SetOutputBuffer(sptr<SurfaceBuffer> output)
{
HLOGI(">>");
std::function<void(ParamSP)> proc = [&](ParamSP msg) {
msg->SetValue("output", output);
};
return DoSyncCall(MsgWhat::SET_OUTPUT_BUFFER, proc);
}
/**************************** public functions end ****************************/
ImageCodec::ImageCodec(OMX_VIDEO_CODINGTYPE codingType, bool isEncoder)
: isEncoder_(isEncoder), codingType_(codingType)
{
debugMode_ = OHOS::system::GetBoolParameter("image.codec.debug", false);
dumpMode_ = OHOS::system::GetBoolParameter("image.codec.dump", false);
LOGI(">> debug mode = %{public}d, dump mode = %{public}d", debugMode_, dumpMode_);
uninitializedState_ = make_shared<UninitializedState>(this);
initializedState_ = make_shared<InitializedState>(this);
startingState_ = make_shared<StartingState>(this);
runningState_ = make_shared<RunningState>(this);
outputPortChangedState_ = make_shared<OutputPortChangedState>(this);
stoppingState_ = make_shared<StoppingState>(this);
StateMachine::ChangeStateTo(uninitializedState_);
}
ImageCodec::~ImageCodec()
{
HLOGI(">>");
MsgHandleLoop::Stop();
ReleaseComponent();
}
int32_t ImageCodec::InitWithName(const string &name)
{
function<void(ParamSP)> proc = [&](ParamSP msg) {
msg->SetValue("name", name);
};
return DoSyncCall(MsgWhat::INIT, proc);
}
const char* ImageCodec::ToString(BufferOwner owner)
{
switch (owner) {
case BufferOwner::OWNED_BY_US:
return "us";
case BufferOwner::OWNED_BY_USER:
return "user";
case BufferOwner::OWNED_BY_OMX:
return "omx";
default:
return "";
}
}
const char* ImageCodec::ToString(MsgWhat what)
{
static const map<MsgWhat, const char*> m = {
{ INIT, "INIT" },
{ SET_CALLBACK, "SET_CALLBACK" },
{ CONFIGURE, "CONFIGURE" },
{ START, "START" },
{ GET_INPUT_FORMAT, "GET_INPUT_FORMAT" },
{ GET_OUTPUT_FORMAT, "GET_OUTPUT_FORMAT" },
{ QUEUE_INPUT_BUFFER, "QUEUE_INPUT_BUFFER" },
{ RELEASE_OUTPUT_BUFFER, "RELEASE_OUTPUT_BUFFER" },
{ RELEASE, "RELEASE" },
{ GET_OUTPUT_BUFFER_USAGE, "GET_OUTPUT_BUFFER_USAGE" },
{ SET_OUTPUT_BUFFER, "SET_OUTPUT_BUFFER" },
{ CODEC_EVENT, "CODEC_EVENT" },
{ OMX_EMPTY_BUFFER_DONE, "OMX_EMPTY_BUFFER_DONE" },
{ OMX_FILL_BUFFER_DONE, "OMX_FILL_BUFFER_DONE" },
{ CHECK_IF_STUCK, "CHECK_IF_STUCK" },
{ FORCE_SHUTDOWN, "FORCE_SHUTDOWN" },
};
auto it = m.find(what);
if (it != m.end()) {
return it->second;
}
return "UNKNOWN";
}
void ImageCodec::ReplyErrorCode(MsgId id, int32_t err)
{
if (id == ASYNC_MSG_ID) {
return;
}
ParamSP reply = ParamBundle::Create();
reply->SetValue("err", err);
PostReply(id, reply);
}
bool ImageCodec::GetPixelFmtFromUser(const Format &format)
{
optional<PixelFmt> fmt;
int32_t graphicFmt;
if (format.GetValue(ImageCodecDescriptionKey::PIXEL_FORMAT, graphicFmt)) {
fmt = TypeConverter::GraphicFmtToFmt(static_cast<GraphicPixelFormat>(graphicFmt));
} else {
HLOGE("pixel format unspecified");
return false;
}
configuredFmt_ = fmt.value();
HLOGI("configured pixel format is %{public}s", configuredFmt_.strFmt.c_str());
return true;
}
optional<double> ImageCodec::GetFrameRateFromUser(const Format &format)
{
double frameRate;
if (format.GetValue(ImageCodecDescriptionKey::FRAME_RATE, frameRate) && frameRate > 0) {
LOGI("user set frame rate %{public}.2f", frameRate);
return frameRate;
}
return nullopt;
}
int32_t ImageCodec::SetFrameRateAdaptiveMode(const Format &format)
{
if (!format.ContainKey(ImageCodecDescriptionKey::VIDEO_FRAME_RATE_ADAPTIVE_MODE)) {
return IC_ERR_UNKNOWN;
}
WorkingFrequencyParam param {};
InitOMXParamExt(param);
if (!GetParameter(OMX_IndexParamWorkingFrequency, param)) {
HLOGW("get working freq param failed");
return IC_ERR_UNKNOWN;
}
HLOGI("level cnt is %{public}d, set level to %{public}d", param.level, param.level - 1);
param.level = param.level - 1;
if (!SetParameter(OMX_IndexParamWorkingFrequency, param)) {
HLOGW("set working freq param failed");
return IC_ERR_UNKNOWN;
}
return IC_ERR_OK;
}
int32_t ImageCodec::SetProcessName(const Format &format)
{
string processName;
if (!format.GetValue(ImageCodecDescriptionKey::PROCESS_NAME, processName)) {
return IC_ERR_UNKNOWN;
}
HLOGI("processName name is %{public}s", processName.c_str());
ProcessNameParam param {};
InitOMXParamExt(param);
if (strcpy_s(param.processName, sizeof(param.processName), processName.c_str()) != EOK) {
HLOGW("strcpy failed");
return IC_ERR_UNKNOWN;
}
if (!SetParameter(OMX_IndexParamProcessName, param)) {
HLOGW("set process name failed");
return IC_ERR_UNKNOWN;
}
return IC_ERR_OK;
}
int32_t ImageCodec::SetVideoPortInfo(OMX_DIRTYPE portIndex, const PortInfo& info)
{
if (info.pixelFmt.has_value()) {
CodecVideoPortFormatParam param;
InitOMXParamExt(param);
param.portIndex = portIndex;
param.codecCompressFormat = info.codingType;
param.codecColorFormat = info.pixelFmt->graphicFmt;
param.framerate = info.frameRate * FRAME_RATE_COEFFICIENT;
if (!SetParameter(OMX_IndexCodecVideoPortFormat, param)) {
HLOGE("set port format failed");
return IC_ERR_UNKNOWN;
}
}
{
OMX_PARAM_PORTDEFINITIONTYPE def;
InitOMXParam(def);
def.nPortIndex = portIndex;
if (!GetParameter(OMX_IndexParamPortDefinition, def)) {
HLOGE("get port definition failed");
return IC_ERR_UNKNOWN;
}
def.format.video.nFrameWidth = info.width;
def.format.video.nFrameHeight = info.height;
def.format.video.eCompressionFormat = info.codingType;
// we dont set eColorFormat here because it has been set by CodecVideoPortFormatParam
def.format.video.xFramerate = info.frameRate * FRAME_RATE_COEFFICIENT;
if (portIndex == OMX_DirInput && info.inputBufSize.has_value()) {
def.nBufferSize = info.inputBufSize.value();
}
if (info.bufferCnt.has_value()) {
def.nBufferCountActual = info.bufferCnt.value();
}
if (!SetParameter(OMX_IndexParamPortDefinition, def)) {
HLOGE("set port definition failed");
return IC_ERR_UNKNOWN;
}
if (portIndex == OMX_DirOutput) {
if (outputFormat_ == nullptr) {
outputFormat_ = make_shared<Format>();
}
outputFormat_->SetValue(ImageCodecDescriptionKey::FRAME_RATE, info.frameRate);
}
}
return (portIndex == OMX_DirInput) ? UpdateInPortFormat() : UpdateOutPortFormat();
}
void ImageCodec::PrintPortDefinition(const OMX_PARAM_PORTDEFINITIONTYPE& def)
{
const OMX_VIDEO_PORTDEFINITIONTYPE& video = def.format.video;
HLOGI("----- %{public}s port definition -----", (def.nPortIndex == OMX_DirInput) ? "INPUT" : "OUTPUT");
HLOGI("bEnabled %{public}d, bPopulated %{public}d", def.bEnabled, def.bPopulated);
HLOGI("nBufferCountActual %{public}u, nBufferSize %{public}u", def.nBufferCountActual, def.nBufferSize);
HLOGI("nFrameWidth x nFrameHeight (%{public}u x %{public}u), framerate %{public}u(%{public}.2f)",
video.nFrameWidth, video.nFrameHeight, video.xFramerate, video.xFramerate / FRAME_RATE_COEFFICIENT);
HLOGI(" nStride x nSliceHeight (%{public}u x %{public}u)", video.nStride, video.nSliceHeight);
HLOGI("eCompressionFormat %{public}d(%{public}#x), eColorFormat %{public}d(%{public}#x)",
video.eCompressionFormat, video.eCompressionFormat, video.eColorFormat, video.eColorFormat);
HLOGI("----------------------------------");
}
int32_t ImageCodec::GetPortDefinition(OMX_DIRTYPE portIndex, OMX_PARAM_PORTDEFINITIONTYPE& def)
{
InitOMXParam(def);
def.nPortIndex = portIndex;
if (!GetParameter(OMX_IndexParamPortDefinition, def)) {
HLOGE("get %{public}s port definition failed", (portIndex == OMX_DirInput ? "input" : "output"));
return IC_ERR_INVALID_VAL;
}
if (def.nBufferSize == 0 || def.nBufferSize > MAX_IMAGE_CODEC_BUFFER_SIZE) {
HLOGE("invalid nBufferSize %{public}u", def.nBufferSize);
return IC_ERR_INVALID_VAL;
}
PrintPortDefinition(def);
return IC_ERR_OK;
}
int32_t ImageCodec::AllocateHardwareBuffers(OMX_DIRTYPE portIndex)
{
HeifPerfTracker tracker(__FUNCTION__);
OMX_PARAM_PORTDEFINITIONTYPE def;
int32_t ret = GetPortDefinition(portIndex, def);
IF_TRUE_RETURN_VAL(ret != IC_ERR_OK, ret);
vector<BufferInfo>& pool = (portIndex == OMX_DirInput) ? inputBufferPool_ : outputBufferPool_;
pool.clear();
for (uint32_t i = 0; i < def.nBufferCountActual; ++i) {
shared_ptr<OmxCodecBuffer> omxBuffer = make_shared<OmxCodecBuffer>();
omxBuffer->size = sizeof(OmxCodecBuffer);
omxBuffer->version.version.majorVersion = 1;
omxBuffer->bufferType = CODEC_BUFFER_TYPE_DMA_MEM_FD;
omxBuffer->fd = -1;
omxBuffer->allocLen = def.nBufferSize;
omxBuffer->fenceFd = -1;
shared_ptr<OmxCodecBuffer> outBuffer = make_shared<OmxCodecBuffer>();
ret = compNode_->AllocateBuffer(portIndex, *omxBuffer, *outBuffer);
if (ret != HDF_SUCCESS) {
HLOGE("Failed to AllocateBuffer on %{public}s port", (portIndex == OMX_DirInput ? "input" : "output"));
return IC_ERR_INVALID_VAL;
}
shared_ptr<ImageCodecBuffer> imgCodecBuffer = ImageCodecBuffer::CreateDmaBuffer(outBuffer->fd,
static_cast<int32_t>(def.nBufferSize), static_cast<int32_t>(def.format.video.nStride));
if (imgCodecBuffer == nullptr || imgCodecBuffer->GetCapacity() != static_cast<int32_t>(def.nBufferSize)) {
HLOGE("AllocateHardwareBuffers failed");
return IC_ERR_NO_MEMORY;
}
BufferInfo bufInfo;
bufInfo.isInput = (portIndex == OMX_DirInput) ? true : false;
bufInfo.owner = BufferOwner::OWNED_BY_US;
bufInfo.surfaceBuffer = nullptr;
bufInfo.imgCodecBuffer = imgCodecBuffer;
bufInfo.omxBuffer = outBuffer;
bufInfo.bufferId = outBuffer->bufferId;
bufInfo.CleanUpUnusedInfo();
pool.push_back(bufInfo);
}
return IC_ERR_OK;
}
int32_t ImageCodec::AllocateSurfaceBuffers(OMX_DIRTYPE portIndex, sptr<SurfaceBuffer> output)
{
HeifPerfTracker tracker(__FUNCTION__);
OMX_PARAM_PORTDEFINITIONTYPE def;
int32_t ret = GetPortDefinition(portIndex, def);
if (ret != IC_ERR_OK) {
return ret;
}
vector<BufferInfo>& pool = (portIndex == OMX_DirInput) ? inputBufferPool_ : outputBufferPool_;
pool.clear();
for (uint32_t i = 0; i < def.nBufferCountActual; ++i) {
shared_ptr<ImageCodecBuffer> imgCodecBuffer = (output != nullptr) ?
ImageCodecBuffer::CreateSurfaceBuffer(output) : ImageCodecBuffer::CreateSurfaceBuffer(requestCfg_);
if (imgCodecBuffer == nullptr) {
HLOGE("AllocateSurfaceBuffers failed");
return IC_ERR_NO_MEMORY;
}
sptr<SurfaceBuffer> surfaceBuffer = imgCodecBuffer->GetSurfaceBuffer();
IF_TRUE_RETURN_VAL_WITH_MSG(surfaceBuffer == nullptr, IC_ERR_INVALID_VAL, "failed to get surfacebuffer");
shared_ptr<OmxCodecBuffer> omxBuffer = isEncoder_ ?
DynamicSurfaceBufferToOmxBuffer() : SurfaceBufferToOmxBuffer(surfaceBuffer);
IF_TRUE_RETURN_VAL(omxBuffer == nullptr, IC_ERR_INVALID_VAL);
shared_ptr<OmxCodecBuffer> outBuffer = make_shared<OmxCodecBuffer>();
int32_t hdiRet = compNode_->UseBuffer(portIndex, *omxBuffer, *outBuffer);
if (hdiRet != HDF_SUCCESS) {
HLOGE("Failed to UseBuffer on %{public}s port", (portIndex == OMX_DirInput ? "input" : "output"));
return IC_ERR_INVALID_VAL;
}
BufferInfo bufInfo;
bufInfo.isInput = (portIndex == OMX_DirInput) ? true : false;
bufInfo.owner = BufferOwner::OWNED_BY_US;
bufInfo.surfaceBuffer = surfaceBuffer;
bufInfo.imgCodecBuffer = imgCodecBuffer;
bufInfo.omxBuffer = outBuffer;
bufInfo.bufferId = outBuffer->bufferId;
pool.push_back(bufInfo);
}
return IC_ERR_OK;
}
shared_ptr<OmxCodecBuffer> ImageCodec::SurfaceBufferToOmxBuffer(const sptr<SurfaceBuffer>& surfaceBuffer)
{
BufferHandle* bufferHandle = surfaceBuffer->GetBufferHandle();
IF_TRUE_RETURN_VAL_WITH_MSG(bufferHandle == nullptr, nullptr, "surfacebuffer has null bufferhandle");
auto omxBuffer = make_shared<OmxCodecBuffer>();
omxBuffer->size = sizeof(OmxCodecBuffer);
omxBuffer->version.version.majorVersion = 1;
omxBuffer->bufferType = CODEC_BUFFER_TYPE_HANDLE;
omxBuffer->bufferhandle = new NativeBuffer(bufferHandle);
omxBuffer->fd = -1;
omxBuffer->allocLen = surfaceBuffer->GetSize();
omxBuffer->fenceFd = -1;
return omxBuffer;
}
shared_ptr<OmxCodecBuffer> ImageCodec::DynamicSurfaceBufferToOmxBuffer()
{
auto omxBuffer = make_shared<OmxCodecBuffer>();
omxBuffer->size = sizeof(OmxCodecBuffer);
omxBuffer->version.version.majorVersion = 1;
omxBuffer->bufferType = CODEC_BUFFER_TYPE_DYNAMIC_HANDLE;
omxBuffer->fd = -1;
omxBuffer->allocLen = 0;
omxBuffer->fenceFd = -1;
return omxBuffer;
}
ImageCodec::BufferInfo* ImageCodec::FindBufferInfoByID(OMX_DIRTYPE portIndex, uint32_t bufferId)
{
vector<BufferInfo>& pool = (portIndex == OMX_DirInput) ? inputBufferPool_ : outputBufferPool_;
for (BufferInfo &info : pool) {
if (info.bufferId == bufferId) {
return &info;
}
}
HLOGE("unknown buffer id %{public}u", bufferId);
return nullptr;
}
optional<size_t> ImageCodec::FindBufferIndexByID(OMX_DIRTYPE portIndex, uint32_t bufferId)
{
const vector<BufferInfo>& pool = (portIndex == OMX_DirInput) ? inputBufferPool_ : outputBufferPool_;
for (size_t i = 0; i < pool.size(); i++) {
if (pool[i].bufferId == bufferId) {
return i;
}
}
HLOGE("unknown buffer id %{public}u", bufferId);
return nullopt;
}
void ImageCodec::NotifyUserToFillThisInBuffer(BufferInfo &info)
{
callback_->OnInputBufferAvailable(info.bufferId, info.imgCodecBuffer);
ChangeOwner(info, BufferOwner::OWNED_BY_USER);
}
void ImageCodec::OnQueueInputBuffer(const MsgInfo &msg, BufferOperationMode mode)
{
uint32_t bufferId;
(void)msg.param->GetValue(BUFFER_ID, bufferId);
BufferInfo* bufferInfo = FindBufferInfoByID(OMX_DirInput, bufferId);
if (bufferInfo == nullptr) {
ReplyErrorCode(msg.id, IC_ERR_INVALID_VAL);
return;
}
if (bufferInfo->owner != BufferOwner::OWNED_BY_USER) {
HLOGE("wrong ownership: buffer id=%{public}d, owner=%{public}s", bufferId, ToString(bufferInfo->owner));
ReplyErrorCode(msg.id, IC_ERR_INVALID_VAL);
return;
}
bufferInfo->imgCodecBuffer->GetBufferCirculateInfo(bufferInfo->omxBuffer->pts,
bufferInfo->omxBuffer->flag,
bufferInfo->omxBuffer->filledLen,
bufferInfo->omxBuffer->offset);
ChangeOwner(*bufferInfo, BufferOwner::OWNED_BY_US);
ReplyErrorCode(msg.id, IC_ERR_OK);
OnQueueInputBuffer(mode, bufferInfo);
}
void ImageCodec::OnQueueInputBuffer(BufferOperationMode mode, BufferInfo* info)
{
switch (mode) {
case KEEP_BUFFER: {
return;
}
case RESUBMIT_BUFFER: {
if (inputPortEos_) {
HLOGI("input already eos, keep this buffer");
return;
}
bool eos = (info->omxBuffer->flag & OMX_BUFFERFLAG_EOS);
if (!eos && info->omxBuffer->filledLen == 0) {
HLOGI("this is not a eos buffer but not filled, ask user to re-fill it");
NotifyUserToFillThisInBuffer(*info);
return;
}
if (eos) {
inputPortEos_ = true;
}
int32_t ret = NotifyOmxToEmptyThisInBuffer(*info);
if (ret != IC_ERR_OK) {
SignalError(IC_ERR_UNKNOWN);
}
return;
}
default: {
HLOGE("SHOULD NEVER BE HERE");
return;
}
}
}
int32_t ImageCodec::NotifyOmxToEmptyThisInBuffer(BufferInfo& info)
{
info.Dump(compUniqueStr_, dumpMode_);
info.EndCpuAccess();
int32_t ret = compNode_->EmptyThisBuffer(*(info.omxBuffer));
if (ret != HDF_SUCCESS) {
HLOGE("EmptyThisBuffer failed");
return IC_ERR_UNKNOWN;
}
ChangeOwner(info, BufferOwner::OWNED_BY_OMX);
return IC_ERR_OK;
}
int32_t ImageCodec::NotifyOmxToFillThisOutBuffer(BufferInfo& info)
{
info.omxBuffer->flag = 0;
int32_t ret = compNode_->FillThisBuffer(*(info.omxBuffer));
if (ret != HDF_SUCCESS) {
HLOGE("outBufId = %{public}u failed", info.bufferId);
return IC_ERR_UNKNOWN;
}
ChangeOwner(info, BufferOwner::OWNED_BY_OMX);
return IC_ERR_OK;
}
void ImageCodec::OnOMXFillBufferDone(const OmxCodecBuffer& omxBuffer, BufferOperationMode mode)
{
optional<size_t> idx = FindBufferIndexByID(OMX_DirOutput, omxBuffer.bufferId);
if (!idx.has_value()) {
return;
}
BufferInfo& info = outputBufferPool_[idx.value()];
if (info.owner != BufferOwner::OWNED_BY_OMX) {
HLOGE("wrong ownership: buffer id=%{public}d, owner=%{public}s", info.bufferId, ToString(info.owner));
return;
}
info.omxBuffer->offset = omxBuffer.offset;
info.omxBuffer->filledLen = omxBuffer.filledLen;
info.omxBuffer->pts = omxBuffer.pts;
info.omxBuffer->flag = omxBuffer.flag;
ChangeOwner(info, BufferOwner::OWNED_BY_US);
OnOMXFillBufferDone(mode, info, idx.value());
}
void ImageCodec::OnOMXFillBufferDone(BufferOperationMode mode, BufferInfo& info, size_t bufferIdx)
{
switch (mode) {
case KEEP_BUFFER:
return;
case RESUBMIT_BUFFER: {
if (outputPortEos_) {
HLOGI("output eos, keep this buffer");
return;
}
bool eos = (info.omxBuffer->flag & OMX_BUFFERFLAG_EOS);
if (!eos && info.omxBuffer->filledLen == 0) {
HLOGI("it's not a eos buffer but not filled, ask omx to re-fill it");
NotifyOmxToFillThisOutBuffer(info);
return;
}
NotifyUserOutBufferAvaliable(info);
if (eos) {
outputPortEos_ = true;
}
return;
}
case FREE_BUFFER:
EraseBufferFromPool(OMX_DirOutput, bufferIdx);
return;
default:
HLOGE("SHOULD NEVER BE HERE");
return;
}
}
void ImageCodec::NotifyUserOutBufferAvaliable(BufferInfo &info)
{
info.BeginCpuAccess();
info.Dump(compUniqueStr_, dumpMode_);
shared_ptr<OmxCodecBuffer> omxBuffer = info.omxBuffer;
info.imgCodecBuffer->SetBufferCirculateInfo(omxBuffer->pts, omxBuffer->flag,
omxBuffer->filledLen, omxBuffer->offset);
callback_->OnOutputBufferAvailable(info.bufferId, info.imgCodecBuffer);
ChangeOwner(info, BufferOwner::OWNED_BY_USER);
}
void ImageCodec::OnReleaseOutputBuffer(const MsgInfo &msg, BufferOperationMode mode)
{
uint32_t bufferId;
(void)msg.param->GetValue(BUFFER_ID, bufferId);
optional<size_t> idx = FindBufferIndexByID(OMX_DirOutput, bufferId);
if (!idx.has_value()) {
ReplyErrorCode(msg.id, IC_ERR_INVALID_VAL);
return;
}
BufferInfo& info = outputBufferPool_[idx.value()];
if (info.owner != BufferOwner::OWNED_BY_USER) {
HLOGE("wrong ownership: buffer id=%{public}d, owner=%{public}s", bufferId, ToString(info.owner));
ReplyErrorCode(msg.id, IC_ERR_INVALID_VAL);
return;
}
HLOGD("outBufId = %{public}u", bufferId);
ChangeOwner(info, BufferOwner::OWNED_BY_US);
ReplyErrorCode(msg.id, IC_ERR_OK);
switch (mode) {
case KEEP_BUFFER: {
return;
}
case RESUBMIT_BUFFER: {
if (outputPortEos_) {
HLOGI("output eos, keep this buffer");
return;
}
int32_t ret = NotifyOmxToFillThisOutBuffer(info);
if (ret != IC_ERR_OK) {
SignalError(IC_ERR_UNKNOWN);
}
return;
}
case FREE_BUFFER: {
EraseBufferFromPool(OMX_DirOutput, idx.value());
return;
}
default: {
HLOGE("SHOULD NEVER BE HERE");
return;
}
}
}
void ImageCodec::ReclaimBuffer(OMX_DIRTYPE portIndex, BufferOwner owner, bool erase)
{
vector<BufferInfo>& pool = (portIndex == OMX_DirInput) ? inputBufferPool_ : outputBufferPool_;
for (size_t i = pool.size(); i > 0;) {
i--;
BufferInfo& info = pool[i];
if (info.owner == owner) {
ChangeOwner(info, BufferOwner::OWNED_BY_US);
if (erase) {
EraseBufferFromPool(portIndex, i);
}
}
}
}
bool ImageCodec::IsAllBufferOwnedByUs(OMX_DIRTYPE portIndex)
{
const vector<BufferInfo>& pool = (portIndex == OMX_DirInput) ? inputBufferPool_ : outputBufferPool_;
for (const BufferInfo& info : pool) {
if (info.owner != BufferOwner::OWNED_BY_US) {
return false;
}
}
return true;
}
bool ImageCodec::IsAllBufferOwnedByUs()
{
return IsAllBufferOwnedByUs(OMX_DirInput) && IsAllBufferOwnedByUs(OMX_DirOutput);
}
void ImageCodec::EraseOutBuffersOwnedByUs()
{
// traverse index in reverse order because we need to erase index from vector
for (size_t i = outputBufferPool_.size(); i > 0;) {
i--;
const BufferInfo& info = outputBufferPool_[i];
if (info.owner == BufferOwner::OWNED_BY_US) {
EraseBufferFromPool(OMX_DirOutput, i);
}
}
}
void ImageCodec::ClearBufferPool(OMX_DIRTYPE portIndex)
{
const vector<BufferInfo>& pool = (portIndex == OMX_DirInput) ? inputBufferPool_ : outputBufferPool_;
for (size_t i = pool.size(); i > 0;) {
i--;
EraseBufferFromPool(portIndex, i);
}
}
void ImageCodec::FreeOmxBuffer(OMX_DIRTYPE portIndex, const BufferInfo& info)
{
if (compNode_ && info.omxBuffer) {
int32_t omxRet = compNode_->FreeBuffer(portIndex, *(info.omxBuffer));
if (omxRet != HDF_SUCCESS) {
HLOGW("notify omx to free buffer failed");
}
}
}
int32_t ImageCodec::DoSyncCall(MsgWhat msgType, function<void(ParamSP)> oper)
{
ParamSP reply;
return DoSyncCallAndGetReply(msgType, oper, reply);
}
int32_t ImageCodec::DoSyncCallAndGetReply(MsgWhat msgType, function<void(ParamSP)> oper, ParamSP &reply)
{
ParamSP msg = ParamBundle::Create();
IF_TRUE_RETURN_VAL_WITH_MSG(msg == nullptr, IC_ERR_NO_MEMORY, "out of memory");
if (oper) {
oper(msg);
}
bool ret = MsgHandleLoop::SendSyncMsg(msgType, msg, reply);
IF_TRUE_RETURN_VAL_WITH_MSG(!ret, IC_ERR_UNKNOWN, "wait msg %{public}d time out", msgType);
int32_t err;
IF_TRUE_RETURN_VAL_WITH_MSG(reply == nullptr || !reply->GetValue("err", err),
IC_ERR_UNKNOWN, "error code of msg %{public}d not replied", msgType);
return err;
}
void ImageCodec::ChangeOmxToTargetState(CodecStateType &state, CodecStateType targetState)
{
int32_t ret = compNode_->SendCommand(CODEC_COMMAND_STATE_SET, targetState, {});
if (ret != HDF_SUCCESS) {
HLOGE("failed to change omx state, ret=%{public}d", ret);
return;
}
int tryCnt = 0;
do {
if (tryCnt++ > 10) { // try up to 10 times
HLOGE("failed to change to state(%{public}d), abort", targetState);
state = CODEC_STATE_INVALID;
break;
}
this_thread::sleep_for(10ms); // wait 10ms
ret = compNode_->GetState(state);
if (ret != HDF_SUCCESS) {
HLOGE("failed to get omx state, ret=%{public}d", ret);
}
} while (ret == HDF_SUCCESS && state != targetState && state != CODEC_STATE_INVALID);
}
bool ImageCodec::RollOmxBackToLoaded()
{
CodecStateType state;
int32_t ret = compNode_->GetState(state);
if (ret != HDF_SUCCESS) {
HLOGE("failed to get omx node status(ret=%{public}d), can not perform state rollback", ret);
return false;
}
HLOGI("current omx state (%{public}d)", state);
switch (state) {
case CODEC_STATE_EXECUTING: {
ChangeOmxToTargetState(state, CODEC_STATE_IDLE);
[[fallthrough]];
}
case CODEC_STATE_IDLE: {
ChangeOmxToTargetState(state, CODEC_STATE_LOADED);
[[fallthrough]];
}
case CODEC_STATE_LOADED:
case CODEC_STATE_INVALID: {
return true;
}
default: {
HLOGE("invalid omx state: %{public}d", state);
return false;
}
}
}
void ImageCodec::CleanUpOmxNode()
{
if (compNode_ == nullptr) {
return;
}
if (RollOmxBackToLoaded()) {
for (const BufferInfo& info : inputBufferPool_) {
FreeOmxBuffer(OMX_DirInput, info);
}
for (const BufferInfo& info : outputBufferPool_) {
FreeOmxBuffer(OMX_DirOutput, info);
}
}
}
void ImageCodec::ReleaseComponent()
{
CleanUpOmxNode();
if (compMgr_ != nullptr) {
compMgr_->DestroyComponent(componentId_);
}
compNode_ = nullptr;
compCb_ = nullptr;
compMgr_ = nullptr;
componentId_ = 0;
componentName_.clear();
}
int32_t ImageCodec::ForceShutdown(int32_t generation)
{
if (generation != stateGeneration_) {
HLOGE("ignoring stale force shutdown message: #%{public}d (now #%{public}d)",
generation, stateGeneration_);
return IC_ERR_OK;
}
HLOGI("force to shutdown");
isShutDownFromRunning_ = true;
notifyCallerAfterShutdownComplete_ = false;
auto err = compNode_->SendCommand(CODEC_COMMAND_STATE_SET, CODEC_STATE_IDLE, {});
if (err == HDF_SUCCESS) {
ChangeStateTo(stoppingState_);
}
return IC_ERR_OK;
}
void ImageCodec::SignalError(ImageCodecError err)
{
HLOGE("fatal error happened: errCode=%{public}d", err);
hasFatalError_ = true;
callback_->OnError(err);
}
void ImageCodec::DeferMessage(const MsgInfo &info)
{
deferredQueue_.push_back(info);
}
void ImageCodec::ProcessDeferredMessages()
{
for (const MsgInfo &info : deferredQueue_) {
StateMachine::OnMsgReceived(info);
}
deferredQueue_.clear();
}
void ImageCodec::ReplyToSyncMsgLater(const MsgInfo& msg)
{
syncMsgToReply_[msg.type].push(make_pair(msg.id, msg.param));
}
bool ImageCodec::GetFirstSyncMsgToReply(MsgInfo& msg)
{
auto iter = syncMsgToReply_.find(msg.type);
if (iter == syncMsgToReply_.end()) {
return false;
}
tie(msg.id, msg.param) = iter->second.front();
iter->second.pop();
return true;
}
/**************************** HdiCallback functions begin ****************************/
int32_t ImageCodec::HdiCallback::EventHandler(CodecEventType event, const EventInfo &info)
{
LOGI("event = %{public}d, data1 = %{public}u, data2 = %{public}u", event, info.data1, info.data2);
ParamSP msg = ParamBundle::Create();
msg->SetValue("event", event);
msg->SetValue("data1", info.data1);
msg->SetValue("data2", info.data2);
codec_->SendAsyncMsg(MsgWhat::CODEC_EVENT, msg);
return HDF_SUCCESS;
}
int32_t ImageCodec::HdiCallback::EmptyBufferDone(int64_t appData, const OmxCodecBuffer& buffer)
{
ParamSP msg = ParamBundle::Create();
msg->SetValue(BUFFER_ID, buffer.bufferId);
codec_->SendAsyncMsg(MsgWhat::OMX_EMPTY_BUFFER_DONE, msg);
return HDF_SUCCESS;
}
int32_t ImageCodec::HdiCallback::FillBufferDone(int64_t appData, const OmxCodecBuffer& buffer)
{
ParamSP msg = ParamBundle::Create();
msg->SetValue("omxBuffer", buffer);
codec_->SendAsyncMsg(MsgWhat::OMX_FILL_BUFFER_DONE, msg);
return HDF_SUCCESS;
}
/**************************** HdiCallback functions begin ****************************/
/**************************** BufferInfo functions begin ****************************/
void ImageCodec::BufferInfo::CleanUpUnusedInfo()
{
if (omxBuffer == nullptr || omxBuffer->fd < 0) {
return;
}
if (omxBuffer->fd == 0) {
LOGW("fd of omxbuffer should never be 0");
}
close(omxBuffer->fd);
omxBuffer->fd = -1;
}
void ImageCodec::BufferInfo::BeginCpuAccess()
{
if (surfaceBuffer && (surfaceBuffer->GetUsage() & BUFFER_USAGE_MEM_MMZ_CACHE)) {
GSError err = surfaceBuffer->InvalidateCache();
if (err != GSERROR_OK) {
LOGW("InvalidateCache failed, GSError=%{public}d", err);
}
}
}
void ImageCodec::BufferInfo::EndCpuAccess()
{
if (surfaceBuffer && (surfaceBuffer->GetUsage() & BUFFER_USAGE_MEM_MMZ_CACHE)) {
GSError err = surfaceBuffer->Map();
if (err != GSERROR_OK) {
LOGW("Map failed, GSError=%{public}d", err);
return;
}
err = surfaceBuffer->FlushCache();
if (err != GSERROR_OK) {
LOGW("FlushCache failed, GSError=%{public}d", err);
}
}
}
/**************************** BufferInfo functions end ****************************/
} // namespace OHOS::ImagePlugin

View File

@ -0,0 +1,139 @@
/*
* Copyright (c) 2024 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "hardware/imagecodec/image_codec_buffer.h"
#include "hardware/imagecodec/image_codec_log.h"
#include "sys/mman.h"
#include "ashmem.h" // commonlibrary/c_utils/base/include/ashmem.h
namespace OHOS::ImagePlugin {
using namespace std;
/*============================ ImageCodecBuffer ====================================*/
std::shared_ptr<ImageCodecBuffer> ImageCodecBuffer::CreateDmaBuffer(int fd, int32_t capacity, int32_t stride)
{
if (fd < 0 || capacity <= 0) {
LOGE("invalid input param: fd=%{public}d, capacity=%{public}d", fd, capacity);
return nullptr;
}
return make_shared<ImageDmaBuffer>(dup(fd), capacity, stride);
}
std::shared_ptr<ImageCodecBuffer> ImageCodecBuffer::CreateSurfaceBuffer(const BufferRequestConfig &config)
{
std::shared_ptr<ImageCodecBuffer> buf = make_shared<ImageSurfaceBuffer>(config);
if (buf != nullptr && buf->Init()) {
return buf;
}
return nullptr;
}
std::shared_ptr<ImageCodecBuffer> ImageCodecBuffer::CreateSurfaceBuffer(sptr<SurfaceBuffer> surface)
{
std::shared_ptr<ImageCodecBuffer> buf = make_shared<ImageSurfaceBuffer>(surface);
return buf;
}
/*============================ ImageDmaBuffer ====================================*/
ImageDmaBuffer::ImageDmaBuffer(int fd, int32_t capacity, int32_t stride): fd_(fd)
{
capacity_ = capacity;
stride_ = stride;
}
ImageDmaBuffer::~ImageDmaBuffer()
{
if (addr_ != nullptr) {
(void)::munmap(addr_, static_cast<size_t>(capacity_));
addr_ = nullptr;
}
if (fd_ >= 0) {
close(fd_);
fd_ = -1;
}
}
uint8_t* ImageDmaBuffer::GetAddr()
{
if (addr_ == nullptr) {
IF_TRUE_RETURN_VAL_WITH_MSG(fd_ < 0, nullptr, "invalid fd=%{public}d", fd_);
unsigned int prot = PROT_READ | PROT_WRITE;
void *addr = ::mmap(nullptr, static_cast<size_t>(capacity_), static_cast<int>(prot), MAP_SHARED, fd_, 0);
IF_TRUE_RETURN_VAL_WITH_MSG(addr == MAP_FAILED, nullptr, "mmap failed for fd=%{public}d", fd_);
addr_ = reinterpret_cast<uint8_t*>(addr);
}
return addr_;
}
/*============================ ImageSurfaceBuffer ====================================*/
ImageSurfaceBuffer::ImageSurfaceBuffer(const BufferRequestConfig &config): config_(config)
{}
ImageSurfaceBuffer::ImageSurfaceBuffer(sptr<SurfaceBuffer> surface)
{
surfaceBuffer_ = surface;
stride_ = surfaceBuffer_->GetStride();
}
ImageSurfaceBuffer::~ImageSurfaceBuffer()
{
if (addr_ != nullptr) {
GSError ret = surfaceBuffer_->Unmap();
if (ret != GSERROR_OK) {
LOGE("failed to unmap surface buffer");
}
addr_ = nullptr;
}
}
bool ImageSurfaceBuffer::Init()
{
surfaceBuffer_ = SurfaceBuffer::Create();
IF_TRUE_RETURN_VAL_WITH_MSG(surfaceBuffer_ == nullptr, false, "failed to create surface buffer");
GSError ret = surfaceBuffer_->Alloc(config_);
IF_TRUE_RETURN_VAL_WITH_MSG(ret != GSERROR_OK, false, "failed to alloc surface buffer");
capacity_ = static_cast<int32_t>(surfaceBuffer_->GetSize());
stride_ = surfaceBuffer_->GetStride();
return true;
}
int32_t ImageSurfaceBuffer::GetFileDescriptor()
{
return surfaceBuffer_->GetFileDescriptor();
}
uint8_t* ImageSurfaceBuffer::GetAddr()
{
if (addr_ == nullptr) {
GSError ret = surfaceBuffer_->Map();
IF_TRUE_RETURN_VAL_WITH_MSG(ret != GSERROR_OK, nullptr,
"mmap failed, err=%{public}s", GSErrorStr(ret).c_str());
addr_ = reinterpret_cast<uint8_t*>(surfaceBuffer_->GetVirAddr());
if (surfaceBuffer_->GetUsage() & BUFFER_USAGE_MEM_MMZ_CACHE) {
ret = surfaceBuffer_->InvalidateCache();
if (ret != GSERROR_OK) {
LOGW("InvalidateCache failed, GSError=%{public}d", ret);
}
}
}
return addr_;
}
sptr<SurfaceBuffer> ImageSurfaceBuffer::GetSurfaceBuffer()
{
return surfaceBuffer_;
}
} // namespace OHOS::ImagePlugin

View File

@ -0,0 +1,254 @@
/*
* Copyright (c) 2024 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <fstream>
#include "hardware/imagecodec/image_codec.h"
#include "hardware/imagecodec/image_codec_log.h"
namespace OHOS::ImagePlugin {
using namespace std;
void ImageCodec::PrintAllBufferInfo()
{
HLOGI("------------INPUT-----------");
for (const BufferInfo& info : inputBufferPool_) {
HLOGI("inBufId = %{public}u, owner = %{public}s", info.bufferId, ToString(info.owner));
}
HLOGI("----------------------------");
HLOGI("------------OUTPUT----------");
for (const BufferInfo& info : outputBufferPool_) {
HLOGI("outBufId = %{public}u, owner = %{public}s", info.bufferId, ToString(info.owner));
}
HLOGI("----------------------------");
}
std::array<uint32_t, ImageCodec::OWNER_CNT> ImageCodec::CountOwner(bool isInput)
{
std::array<uint32_t, OWNER_CNT> arr;
arr.fill(0);
const vector<BufferInfo>& pool = isInput ? inputBufferPool_ : outputBufferPool_;
for (const BufferInfo &info : pool) {
arr[info.owner]++;
}
return arr;
}
void ImageCodec::ChangeOwner(BufferInfo& info, BufferOwner newOwner)
{
if (!debugMode_) {
info.owner = newOwner;
return;
}
BufferOwner oldOwner = info.owner;
const char* oldOwnerStr = ToString(oldOwner);
const char* newOwnerStr = ToString(newOwner);
const char* idStr = info.isInput ? "inBufId" : "outBufId";
// calculate hold time
auto now = chrono::steady_clock::now();
uint64_t holdUs = chrono::duration_cast<chrono::microseconds>(now - info.lastOwnerChangeTime).count();
double holdMs = holdUs / US_TO_MS;
TotalCntAndCost& holdRecord = info.isInput ? inputHoldTimeRecord_[oldOwner][newOwner] :
outputHoldTimeRecord_[oldOwner][newOwner];
holdRecord.totalCnt++;
holdRecord.totalCostUs += holdUs;
double aveHoldMs = holdRecord.totalCostUs / US_TO_MS / holdRecord.totalCnt;
// now change owner
info.lastOwnerChangeTime = now;
info.owner = newOwner;
std::array<uint32_t, OWNER_CNT> arr = CountOwner(info.isInput);
HLOGI("%{public}s = %{public}u, after hold %{public}.1f ms (%{public}.1f ms), %{public}s -> %{public}s, "
"%{public}u/%{public}u/%{public}u",
idStr, info.bufferId, holdMs, aveHoldMs, oldOwnerStr, newOwnerStr,
arr[OWNED_BY_US], arr[OWNED_BY_USER], arr[OWNED_BY_OMX]);
if (info.isInput && oldOwner == OWNED_BY_US && newOwner == OWNED_BY_OMX) {
UpdateInputRecord(info, now);
}
if (!info.isInput && oldOwner == OWNED_BY_OMX && newOwner == OWNED_BY_US) {
UpdateOutputRecord(info, now);
}
}
void ImageCodec::UpdateInputRecord(const BufferInfo& info, std::chrono::time_point<std::chrono::steady_clock> now)
{
if (!info.IsValidFrame()) {
return;
}
inTimeMap_[info.omxBuffer->pts] = now;
if (inTotalCnt_ == 0) {
firstInTime_ = now;
}
inTotalCnt_++;
uint64_t fromFirstInToNow = chrono::duration_cast<chrono::microseconds>(now - firstInTime_).count();
if (fromFirstInToNow == 0) {
HLOGI("pts = %{public}" PRId64 ", len = %{public}u, flags = 0x%{public}x",
info.omxBuffer->pts, info.omxBuffer->filledLen, info.omxBuffer->flag);
} else {
double inFps = inTotalCnt_ * US_TO_S / fromFirstInToNow;
HLOGI("pts = %{public}" PRId64 ", len = %{public}u, flags = 0x%{public}x, in fps %{public}.2f",
info.omxBuffer->pts, info.omxBuffer->filledLen, info.omxBuffer->flag, inFps);
}
}
void ImageCodec::UpdateOutputRecord(const BufferInfo& info, std::chrono::time_point<std::chrono::steady_clock> now)
{
if (!info.IsValidFrame()) {
return;
}
auto it = inTimeMap_.find(info.omxBuffer->pts);
if (it == inTimeMap_.end()) {
return;
}
if (outRecord_.totalCnt == 0) {
firstOutTime_ = now;
}
outRecord_.totalCnt++;
uint64_t fromInToOut = chrono::duration_cast<chrono::microseconds>(now - it->second).count();
inTimeMap_.erase(it);
outRecord_.totalCostUs += fromInToOut;
double oneFrameCostMs = fromInToOut / US_TO_MS;
double averageCostMs = outRecord_.totalCostUs / US_TO_MS / outRecord_.totalCnt;
uint64_t fromFirstOutToNow = chrono::duration_cast<chrono::microseconds>(now - firstOutTime_).count();
if (fromFirstOutToNow == 0) {
HLOGI("pts = %{public}" PRId64 ", len = %{public}u, flags = 0x%{public}x, "
"cost %{public}.2f ms (%{public}.2f ms)",
info.omxBuffer->pts, info.omxBuffer->filledLen, info.omxBuffer->flag,
oneFrameCostMs, averageCostMs);
} else {
double outFps = outRecord_.totalCnt * US_TO_S / fromFirstOutToNow;
HLOGI("pts = %{public}" PRId64 ", len = %{public}u, flags = 0x%{public}x, "
"cost %{public}.2f ms (%{public}.2f ms), out fps %{public}.2f",
info.omxBuffer->pts, info.omxBuffer->filledLen, info.omxBuffer->flag,
oneFrameCostMs, averageCostMs, outFps);
}
}
bool ImageCodec::BufferInfo::IsValidFrame() const
{
if (omxBuffer->flag & OMX_BUFFERFLAG_EOS) {
return false;
}
if (omxBuffer->flag & OMX_BUFFERFLAG_CODECCONFIG) {
return false;
}
if (omxBuffer->filledLen == 0) {
return false;
}
return true;
}
void ImageCodec::BufferInfo::Dump(const string& prefix, bool dumpMode) const
{
if (!dumpMode) {
return;
}
if (isInput) {
Dump(prefix + "_Input");
} else {
Dump(prefix + "_Output");
}
}
void ImageCodec::BufferInfo::Dump(const string& prefix) const
{
if (surfaceBuffer) {
DumpSurfaceBuffer(prefix);
} else {
DumpLinearBuffer(prefix);
}
}
void ImageCodec::BufferInfo::DumpSurfaceBuffer(const std::string& prefix) const
{
const char* va = reinterpret_cast<const char*>(surfaceBuffer->GetVirAddr());
if (va == nullptr) {
LOGW("surface buffer has null va");
return;
}
bool eos = (omxBuffer->flag & OMX_BUFFERFLAG_EOS);
if (eos || omxBuffer->filledLen == 0) {
return;
}
int w = surfaceBuffer->GetWidth();
int h = surfaceBuffer->GetHeight();
int alignedW = surfaceBuffer->GetStride();
uint32_t totalSize = surfaceBuffer->GetSize();
if (w <= 0 || h <= 0 || alignedW <= 0 || w > alignedW) {
LOGW("invalid buffer dimension");
return;
}
std::optional<PixelFmt> fmt = TypeConverter::GraphicFmtToFmt(
static_cast<GraphicPixelFormat>(surfaceBuffer->GetFormat()));
if (fmt == nullopt) {
LOGW("invalid fmt=%{public}d", surfaceBuffer->GetFormat());
return;
}
char name[128];
int ret = sprintf_s(name, sizeof(name), "%s/%s_%dx%d(%d)_fmt%s_pts%" PRId64 ".yuv",
DUMP_PATH, prefix.c_str(), w, h, alignedW, fmt->strFmt.c_str(), omxBuffer->pts);
if (ret > 0) {
ofstream ofs(name, ios::binary);
if (ofs.is_open()) {
ofs.write(va, totalSize);
} else {
LOGW("cannot open %{public}s", name);
}
}
// if we unmap here, flush cache will fail
}
void ImageCodec::BufferInfo::DumpLinearBuffer(const string& prefix) const
{
if (imgCodecBuffer == nullptr) {
LOGW("invalid imgCodecBuffer");
return;
}
const char* va = reinterpret_cast<const char*>(imgCodecBuffer->GetAddr());
if (va == nullptr) {
LOGW("null va");
return;
}
bool eos = (omxBuffer->flag & OMX_BUFFERFLAG_EOS);
if (eos || omxBuffer->filledLen == 0) {
return;
}
char name[128];
int ret = 0;
if (isInput) {
ret = sprintf_s(name, sizeof(name), "%s/%s.bin", DUMP_PATH, prefix.c_str());
} else {
ret = sprintf_s(name, sizeof(name), "%s/%s_(%d)_pts%" PRId64 ".yuv",
DUMP_PATH, prefix.c_str(), imgCodecBuffer->GetStride(), omxBuffer->pts);
}
if (ret <= 0) {
LOGW("sprintf_s failed");
return;
}
std::ios_base::openmode mode = isInput ? (ios::binary | ios::app) : ios::binary;
ofstream ofs(name, mode);
if (ofs.is_open()) {
ofs.write(va, omxBuffer->filledLen);
} else {
LOGW("cannot open %{public}s", name);
}
}
} // namespace OHOS::ImagePlugin

View File

@ -0,0 +1,92 @@
/*
* Copyright (c) 2024 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "hardware/imagecodec/image_codec_list.h"
#include "hardware/imagecodec/image_codec_log.h"
#include "iservmgr_hdi.h" // drivers/hdf_core/interfaces/inner_api/hdi/
#include "syspara/parameters.h" // base/startup/init/interfaces/innerkits/include/
namespace OHOS::ImagePlugin {
using namespace std;
using namespace OHOS::HDI::ServiceManager::V1_0;
using namespace HdiCodecNamespace;
static mutex g_heifCodecMtx;
static sptr<ICodecComponentManager> g_compMgrForHeif;
class Listener : public ServStatListenerStub {
public:
void OnReceive(const ServiceStatus &status) override
{
if (status.serviceName == "codec_component_manager_service" && status.status == SERVIE_STATUS_STOP) {
LOGW("codec_component_manager_service died");
lock_guard<mutex> lk(g_heifCodecMtx);
g_compMgrForHeif = nullptr;
}
}
};
static bool IsPassthrough()
{
static bool usePassthrough = OHOS::system::GetBoolParameter("image.codec.usePassthrough", false);
LOGI("%{public}s mode", usePassthrough ? "passthrough" : "ipc");
return usePassthrough;
}
sptr<ICodecComponentManager> GetManager()
{
lock_guard<mutex> lk(g_heifCodecMtx);
if (g_compMgrForHeif) {
return g_compMgrForHeif;
}
LOGI("need to get ICodecComponentManager");
bool isPassthrough = IsPassthrough();
if (!isPassthrough) {
sptr<IServiceManager> serviceMng = IServiceManager::Get();
if (serviceMng) {
serviceMng->RegisterServiceStatusListener(new Listener(), DEVICE_CLASS_DEFAULT);
}
}
g_compMgrForHeif = ICodecComponentManager::Get(isPassthrough);
return g_compMgrForHeif;
}
vector<CodecCompCapability> GetCapList()
{
sptr<ICodecComponentManager> mnger = GetManager();
if (mnger == nullptr) {
LOGE("failed to create codec component manager");
return {};
}
int32_t compCnt = 0;
int32_t ret = mnger->GetComponentNum(compCnt);
if (ret != HDF_SUCCESS || compCnt <= 0) {
LOGE("failed to query component number, ret=%{public}d", ret);
return {};
}
std::vector<CodecCompCapability> capList(compCnt);
ret = mnger->GetComponentCapabilityList(capList, compCnt);
if (ret != HDF_SUCCESS) {
LOGE("failed to query component capability list, ret=%{public}d", ret);
return {};
}
if (capList.empty()) {
LOGE("GetComponentCapabilityList return empty");
} else {
LOGI("GetComponentCapabilityList return %{public}zu components", capList.size());
}
return capList;
}
} // namespace OHOS::ImagePlugin

View File

@ -0,0 +1,405 @@
/*
* Copyright (c) 2024 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "hardware/imagecodec/image_decoder.h"
#include "hardware/imagecodec/image_codec_log.h"
#include <cassert>
#include "codec_omx_ext.h" // drivers/peripheral/codec/interfaces/include/codec_omx_ext.h
#include "OMX_VideoExt.h" // third_party/openmax/api/1.1.2/OMX_VideoExt.h
#include "surface_buffer.h" // foundation/graphic/graphic_surface/interfaces/inner_api/surface/surface_buffer.h
namespace OHOS::ImagePlugin {
using namespace std;
ImageDecoder::ImageDecoder()
: ImageCodec(static_cast<OMX_VIDEO_CODINGTYPE>(CODEC_OMX_VIDEO_CodingHEVC), false)
{}
int32_t ImageDecoder::OnConfigure(const Format &format)
{
configFormat_ = make_shared<Format>(format);
(void)format.GetValue(ImageCodecDescriptionKey::ENABLE_HEIF_GRID, enableHeifGrid_);
UseBufferType useBufferTypes;
InitOMXParamExt(useBufferTypes);
useBufferTypes.portIndex = OMX_DirOutput;
useBufferTypes.bufferType = CODEC_BUFFER_TYPE_HANDLE;
if (!SetParameter(OMX_IndexParamUseBufferType, useBufferTypes)) {
HLOGE("component don't support CODEC_BUFFER_TYPE_HANDLE");
return IC_ERR_INVALID_VAL;
}
(void)SetProcessName(format);
(void)SetFrameRateAdaptiveMode(format);
return SetupPort(format);
}
int32_t ImageDecoder::SetupPort(const Format &format)
{
uint32_t width;
if (!format.GetValue(ImageCodecDescriptionKey::WIDTH, width) || width <= 0) {
HLOGE("format should contain width");
return IC_ERR_INVALID_VAL;
}
uint32_t height;
if (!format.GetValue(ImageCodecDescriptionKey::HEIGHT, height) || height <= 0) {
HLOGE("format should contain height");
return IC_ERR_INVALID_VAL;
}
HLOGI("user set width %{public}u, height %{public}u", width, height);
if (!GetPixelFmtFromUser(format)) {
return IC_ERR_INVALID_VAL;
}
uint32_t inputBufferCnt = 0;
(void)format.GetValue(ImageCodecDescriptionKey::INPUT_BUFFER_COUNT, inputBufferCnt);
uint32_t outputBufferCnt = 0;
(void)format.GetValue(ImageCodecDescriptionKey::OUTPUT_BUFFER_COUNT, outputBufferCnt);
optional<double> frameRate = GetFrameRateFromUser(format);
if (!frameRate.has_value()) {
HLOGI("user don't set valid frame rate, use default 30.0");
frameRate = 30.0; // default frame rate 30.0
}
PortInfo inputPortInfo {width, height, codingType_, std::nullopt, frameRate.value()};
int32_t maxInputSize = 0;
(void)format.GetValue(ImageCodecDescriptionKey::MAX_INPUT_SIZE, maxInputSize);
if (maxInputSize > 0) {
inputPortInfo.inputBufSize = static_cast<uint32_t>(maxInputSize);
}
if (inputBufferCnt > 0) {
inputPortInfo.bufferCnt = inputBufferCnt;
}
int32_t ret = SetVideoPortInfo(OMX_DirInput, inputPortInfo);
if (ret != IC_ERR_OK) {
return ret;
}
PortInfo outputPortInfo = {width, height, OMX_VIDEO_CodingUnused, configuredFmt_, frameRate.value()};
if (outputBufferCnt > 0) {
outputPortInfo.bufferCnt = outputBufferCnt;
}
ret = SetVideoPortInfo(OMX_DirOutput, outputPortInfo);
if (ret != IC_ERR_OK) {
return ret;
}
return IC_ERR_OK;
}
bool ImageDecoder::UpdateConfiguredFmt(OMX_COLOR_FORMATTYPE portFmt)
{
auto graphicFmt = static_cast<GraphicPixelFormat>(portFmt);
if (graphicFmt != configuredFmt_.graphicFmt) {
optional<PixelFmt> fmt = TypeConverter::GraphicFmtToFmt(graphicFmt);
if (!fmt.has_value()) {
return false;
}
HLOGI("GraphicPixelFormat need update: configured(%{public}s) -> portdefinition(%{public}s)",
configuredFmt_.strFmt.c_str(), fmt->strFmt.c_str());
configuredFmt_ = fmt.value();
}
return true;
}
int32_t ImageDecoder::UpdateInPortFormat()
{
OMX_PARAM_PORTDEFINITIONTYPE def;
InitOMXParam(def);
def.nPortIndex = OMX_DirInput;
if (!GetParameter(OMX_IndexParamPortDefinition, def)) {
HLOGE("get input port definition failed");
return IC_ERR_UNKNOWN;
}
PrintPortDefinition(def);
if (inputFormat_ == nullptr) {
inputFormat_ = make_shared<Format>();
}
inputFormat_->SetValue(ImageCodecDescriptionKey::WIDTH, def.format.video.nFrameWidth);
inputFormat_->SetValue(ImageCodecDescriptionKey::HEIGHT, def.format.video.nFrameHeight);
return IC_ERR_OK;
}
int32_t ImageDecoder::UpdateOutPortFormat()
{
OMX_PARAM_PORTDEFINITIONTYPE def;
InitOMXParam(def);
def.nPortIndex = OMX_DirOutput;
if (!GetParameter(OMX_IndexParamPortDefinition, def)) {
HLOGE("get output port definition failed");
return IC_ERR_UNKNOWN;
}
PrintPortDefinition(def);
if (def.nBufferCountActual == 0) {
HLOGE("invalid bufferCount");
return IC_ERR_UNKNOWN;
}
(void)UpdateConfiguredFmt(def.format.video.eColorFormat);
uint32_t w = def.format.video.nFrameWidth;
uint32_t h = def.format.video.nFrameHeight;
// save into member variable
requestCfg_.timeout = 0;
requestCfg_.width = static_cast<int32_t>(w);
requestCfg_.height = static_cast<int32_t>(h);
requestCfg_.strideAlignment = STRIDE_ALIGNMENT;
requestCfg_.format = configuredFmt_.graphicFmt;
requestCfg_.usage = GetProducerUsage();
UpdateDisplaySizeByCrop();
// save into format
if (outputFormat_ == nullptr) {
outputFormat_ = make_shared<Format>();
}
if (!outputFormat_->ContainKey(ImageCodecDescriptionKey::WIDTH)) {
outputFormat_->SetValue(ImageCodecDescriptionKey::WIDTH, w);
}
if (!outputFormat_->ContainKey(ImageCodecDescriptionKey::HEIGHT)) {
outputFormat_->SetValue(ImageCodecDescriptionKey::HEIGHT, h);
}
outputFormat_->SetValue(ImageCodecDescriptionKey::VIDEO_DISPLAY_WIDTH, requestCfg_.width);
outputFormat_->SetValue(ImageCodecDescriptionKey::VIDEO_DISPLAY_HEIGHT, requestCfg_.height);
outputFormat_->SetValue(ImageCodecDescriptionKey::PIXEL_FORMAT,
static_cast<int32_t>(configuredFmt_.graphicFmt));
return IC_ERR_OK;
}
void ImageDecoder::UpdateColorAspects()
{
CodecVideoColorspace param;
InitOMXParamExt(param);
param.portIndex = OMX_DirOutput;
if (!GetParameter(OMX_IndexColorAspects, param, true)) {
return;
}
HLOGI("range:%{public}d, primary:%{public}d, transfer:%{public}d, matrix:%{public}d)",
param.aspects.range, param.aspects.primaries, param.aspects.transfer, param.aspects.matrixCoeffs);
if (outputFormat_) {
outputFormat_->SetValue(ImageCodecDescriptionKey::RANGE_FLAG, param.aspects.range);
outputFormat_->SetValue(ImageCodecDescriptionKey::COLOR_PRIMARIES, param.aspects.primaries);
outputFormat_->SetValue(ImageCodecDescriptionKey::TRANSFER_CHARACTERISTICS, param.aspects.transfer);
outputFormat_->SetValue(ImageCodecDescriptionKey::MATRIX_COEFFICIENTS, param.aspects.matrixCoeffs);
callback_->OnOutputFormatChanged(*(outputFormat_.get()));
}
}
void ImageDecoder::UpdateDisplaySizeByCrop()
{
OMX_CONFIG_RECTTYPE rect;
InitOMXParam(rect);
rect.nPortIndex = OMX_DirOutput;
if (!GetParameter(OMX_IndexConfigCommonOutputCrop, rect, true)) {
HLOGW("get crop failed, use default");
return;
}
if (rect.nLeft < 0 || rect.nTop < 0 || rect.nWidth == 0 || rect.nHeight == 0 ||
static_cast<int32_t>(rect.nLeft) + static_cast<int32_t>(rect.nWidth) > requestCfg_.width ||
static_cast<int32_t>(rect.nTop) + static_cast<int32_t>(rect.nHeight) > requestCfg_.height) {
HLOGW("wrong crop rect (%{public}d, %{public}d, %{public}u, %{public}u), use default",
rect.nLeft, rect.nTop, rect.nWidth, rect.nHeight);
return;
}
HLOGI("crop rect (%{public}d, %{public}d, %{public}u, %{public}u)",
rect.nLeft, rect.nTop, rect.nWidth, rect.nHeight);
requestCfg_.width = static_cast<int32_t>(rect.nWidth);
requestCfg_.height = static_cast<int32_t>(rect.nHeight);
}
int32_t ImageDecoder::ReConfigureOutputBufferCnt()
{
IF_TRUE_RETURN_VAL(enableHeifGrid_, IC_ERR_OK);
OMX_PARAM_PORTDEFINITIONTYPE def;
InitOMXParam(def);
def.nPortIndex = OMX_DirOutput;
IF_TRUE_RETURN_VAL_WITH_MSG(!GetParameter(OMX_IndexParamPortDefinition, def), IC_ERR_UNKNOWN,
"failed to get output port def");
uint32_t outputBufferCnt = 0;
(void)configFormat_->GetValue(ImageCodecDescriptionKey::OUTPUT_BUFFER_COUNT, outputBufferCnt);
IF_TRUE_RETURN_VAL_WITH_MSG(outputBufferCnt == 0, IC_ERR_UNKNOWN, "failed to get output buffer cnt");
def.nBufferCountActual = outputBufferCnt;
IF_TRUE_RETURN_VAL_WITH_MSG(!SetParameter(OMX_IndexParamPortDefinition, def), IC_ERR_UNKNOWN,
"failed to set output port def");
return IC_ERR_OK;
}
uint64_t ImageDecoder::GetProducerUsage()
{
uint64_t producerUsage = BUFFER_MODE_REQUEST_USAGE;
GetBufferHandleUsageParams vendorUsage;
InitOMXParamExt(vendorUsage);
vendorUsage.portIndex = static_cast<uint32_t>(OMX_DirOutput);
if (GetParameter(OMX_IndexParamGetBufferHandleUsage, vendorUsage)) {
HLOGI("vendor producer usage = 0x%" PRIx64 "", vendorUsage.usage);
producerUsage |= vendorUsage.usage;
}
HLOGI("decoder producer usage = 0x%" PRIx64 "", producerUsage);
return producerUsage;
}
uint64_t ImageDecoder::OnGetOutputBufferUsage()
{
uint64_t usage = GetProducerUsage();
usage |= BUFFER_USAGE_CPU_WRITE;
return usage;
}
int32_t ImageDecoder::OnSetOutputBuffer(sptr<SurfaceBuffer> output)
{
if (output == nullptr) {
HLOGE("invalid output buffer");
return IC_ERR_INVALID_VAL;
}
outputBuffer_ = output;
return IC_ERR_OK;
}
bool ImageDecoder::ReadyToStart()
{
if (callback_ == nullptr || outputFormat_ == nullptr || inputFormat_ == nullptr) {
HLOGE("callback not set or format is not configured, can't start");
return false;
}
if (enableHeifGrid_ && outputBuffer_ != nullptr) {
HLOGE("can not set output buffer when heif grid is enabled");
return false;
}
if (!enableHeifGrid_ && outputBuffer_ == nullptr) {
HLOGE("must set output buffer when heif grid is not enabled");
return false;
}
return true;
}
int32_t ImageDecoder::AllocateBuffersOnPort(OMX_DIRTYPE portIndex)
{
if (portIndex == OMX_DirInput) {
return AllocateHardwareBuffers(portIndex);
}
int32_t ret = AllocateSurfaceBuffers(portIndex, outputBuffer_);
if (ret == IC_ERR_OK) {
UpdateFormatFromSurfaceBuffer();
}
return ret;
}
void ImageDecoder::UpdateFormatFromSurfaceBuffer()
{
if (outputBufferPool_.empty()) {
return;
}
sptr<SurfaceBuffer> surfaceBuffer = outputBufferPool_.front().surfaceBuffer;
if (surfaceBuffer == nullptr) {
return;
}
outputFormat_->SetValue(ImageCodecDescriptionKey::VIDEO_DISPLAY_WIDTH, surfaceBuffer->GetWidth());
outputFormat_->SetValue(ImageCodecDescriptionKey::VIDEO_DISPLAY_HEIGHT, surfaceBuffer->GetHeight());
outputFormat_->SetValue(ImageCodecDescriptionKey::WIDTH, surfaceBuffer->GetStride());
OMX_PARAM_PORTDEFINITIONTYPE def;
int32_t ret = GetPortDefinition(OMX_DirOutput, def);
int32_t sliceHeight = static_cast<int32_t>(def.format.video.nSliceHeight);
if (ret == IC_ERR_OK && sliceHeight >= surfaceBuffer->GetHeight()) {
outputFormat_->SetValue(ImageCodecDescriptionKey::HEIGHT, sliceHeight);
}
}
int32_t ImageDecoder::SubmitAllBuffersOwnedByUs()
{
HLOGI(">>");
if (isBufferCirculating_) {
HLOGI("buffer is already circulating, no need to do again");
return IC_ERR_OK;
}
int32_t ret = SubmitOutputBuffersToOmxNode();
if (ret != IC_ERR_OK) {
return ret;
}
for (BufferInfo& info : inputBufferPool_) {
if (info.owner == BufferOwner::OWNED_BY_US) {
NotifyUserToFillThisInBuffer(info);
}
}
isBufferCirculating_ = true;
return IC_ERR_OK;
}
int32_t ImageDecoder::SubmitOutputBuffersToOmxNode()
{
for (BufferInfo& info : outputBufferPool_) {
switch (info.owner) {
case BufferOwner::OWNED_BY_US: {
int32_t ret = NotifyOmxToFillThisOutBuffer(info);
if (ret != IC_ERR_OK) {
return ret;
}
continue;
}
case BufferOwner::OWNED_BY_OMX: {
continue;
}
default: {
HLOGE("buffer id %{public}u has invalid owner %{public}d", info.bufferId, info.owner);
return IC_ERR_UNKNOWN;
}
}
}
return IC_ERR_OK;
}
void ImageDecoder::OnOMXEmptyBufferDone(uint32_t bufferId, BufferOperationMode mode)
{
BufferInfo *info = FindBufferInfoByID(OMX_DirInput, bufferId);
if (info == nullptr) {
HLOGE("unknown buffer id %{public}u", bufferId);
return;
}
if (info->owner != BufferOwner::OWNED_BY_OMX) {
HLOGE("wrong ownership: buffer id=%{public}d, owner=%{public}s", bufferId, ToString(info->owner));
return;
}
ChangeOwner(*info, BufferOwner::OWNED_BY_US);
switch (mode) {
case KEEP_BUFFER:
return;
case RESUBMIT_BUFFER: {
if (!inputPortEos_) {
NotifyUserToFillThisInBuffer(*info);
}
return;
}
default: {
HLOGE("SHOULD NEVER BE HERE");
return;
}
}
}
void ImageDecoder::EraseBufferFromPool(OMX_DIRTYPE portIndex, size_t i)
{
vector<BufferInfo>& pool = (portIndex == OMX_DirInput) ? inputBufferPool_ : outputBufferPool_;
if (i >= pool.size()) {
return;
}
BufferInfo& info = pool[i];
FreeOmxBuffer(portIndex, info);
pool.erase(pool.begin() + i);
}
} // namespace OHOS::ImagePlugin

View File

@ -0,0 +1,146 @@
/*
* Copyright (c) 2024 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "hardware/imagecodec/msg_handle_loop.h"
#include <chrono>
#include <cinttypes>
#include "qos.h"
#include "hardware/imagecodec/image_codec_log.h"
namespace OHOS::ImagePlugin {
using namespace std;
MsgHandleLoop::MsgHandleLoop()
{
m_thread = thread(&MsgHandleLoop::MainLoop, this);
}
MsgHandleLoop::~MsgHandleLoop()
{
Stop();
}
void MsgHandleLoop::Stop()
{
{
lock_guard<mutex> lock(m_mtx);
m_threadNeedStop = true;
m_threadCond.notify_all();
}
if (m_thread.joinable()) {
m_thread.join();
}
}
void MsgHandleLoop::SendAsyncMsg(MsgType type, const ParamSP &msg, uint32_t delayUs)
{
lock_guard<mutex> lock(m_mtx);
TimeUs nowUs = GetNowUs();
TimeUs msgProcessTime = (delayUs > INT64_MAX - nowUs) ? INT64_MAX : (nowUs + delayUs);
if (m_msgQueue.find(msgProcessTime) != m_msgQueue.end()) {
LOGW("DUPLICATIVE MSG TIMESTAMP!!!");
msgProcessTime++;
}
m_msgQueue[msgProcessTime] = MsgInfo {type, ASYNC_MSG_ID, msg};
m_threadCond.notify_all();
}
bool MsgHandleLoop::SendSyncMsg(MsgType type, const ParamSP &msg, ParamSP &reply, uint32_t waitMs)
{
MsgId id = GenerateMsgId();
{
lock_guard<mutex> lock(m_mtx);
TimeUs time = GetNowUs();
if (m_msgQueue.find(time) != m_msgQueue.end()) {
LOGW("DUPLICATIVE MSG TIMESTAMP!!!");
time++;
}
m_msgQueue[time] = MsgInfo {type, id, msg};
m_threadCond.notify_all();
}
unique_lock<mutex> lock(m_replyMtx);
const auto pred = [this, id]() {
return m_replies.find(id) != m_replies.end();
};
if (waitMs == 0) {
m_replyCond.wait(lock, pred);
} else {
if (!m_replyCond.wait_for(lock, chrono::milliseconds(waitMs), pred)) {
LOGE("type=%{public}u wait reply timeout", type);
return false;
}
}
reply = m_replies[id];
m_replies.erase(id);
return true;
}
void MsgHandleLoop::PostReply(MsgId id, const ParamSP &reply)
{
if (id == ASYNC_MSG_ID) {
return;
}
lock_guard<mutex> lock(m_replyMtx);
m_replies[id] = reply;
m_replyCond.notify_all();
}
MsgId MsgHandleLoop::GenerateMsgId()
{
lock_guard<mutex> lock(m_mtx);
m_lastMsgId++;
if (m_lastMsgId == ASYNC_MSG_ID) {
m_lastMsgId++;
}
return m_lastMsgId;
}
void MsgHandleLoop::MainLoop()
{
LOGI("increase thread priority");
pthread_setname_np(pthread_self(), "OS_ImageCodecLoop");
OHOS::QOS::SetThreadQos(OHOS::QOS::QosLevel::QOS_USER_INTERACTIVE);
while (true) {
MsgInfo info;
{
unique_lock<mutex> lock(m_mtx);
m_threadCond.wait(lock, [this] {
return m_threadNeedStop || !m_msgQueue.empty();
});
if (m_threadNeedStop) {
LOGI("stopped, remain %{public}zu msg unprocessed", m_msgQueue.size());
break;
}
TimeUs processUs = m_msgQueue.begin()->first;
TimeUs nowUs = GetNowUs();
if (processUs > nowUs) {
m_threadCond.wait_for(lock, chrono::microseconds(processUs - nowUs));
continue;
}
info = m_msgQueue.begin()->second;
m_msgQueue.erase(m_msgQueue.begin());
}
OnMsgReceived(info);
}
}
MsgHandleLoop::TimeUs MsgHandleLoop::GetNowUs()
{
auto now = chrono::steady_clock::now();
return chrono::duration_cast<chrono::microseconds>(now.time_since_epoch()).count();
}
} // namespace OHOS::ImagePlugin

View File

@ -0,0 +1,29 @@
/*
* Copyright (c) 2024 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "hardware/imagecodec/param_bundle.h"
#include <cinttypes>
namespace OHOS::ImagePlugin {
using namespace std;
ParamSP ParamBundle::Create()
{
return {new(nothrow)ParamBundle(),
[](ParamBundle *param) {
delete param;
}};
}
} // namespace OHOS::ImagePlugin

View File

@ -0,0 +1,41 @@
/*
* Copyright (c) 2024 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "hardware/imagecodec/state_machine.h"
#include "hardware/imagecodec/image_codec_log.h"
namespace OHOS::ImagePlugin {
void StateMachine::ChangeStateTo(const std::shared_ptr<State> &targetState)
{
if (currState_ == targetState) {
LOGI("already %{public}s", currState_->stateName_.c_str());
return;
}
std::shared_ptr<State> lastState = currState_;
currState_ = targetState;
if (lastState == nullptr) {
LOGI("change to %{public}s", currState_->stateName_.c_str());
} else {
LOGI("%{public}s -> %{public}s", lastState->stateName_.c_str(), currState_->stateName_.c_str());
lastState->OnStateExited();
}
currState_->OnStateEntered();
}
void StateMachine::OnMsgReceived(const MsgInfo &info)
{
currState_->OnMsgReceived(info);
}
} // namespace OHOS::ImagePlugin

View File

@ -0,0 +1,40 @@
/*
* Copyright (c) 2024 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "hardware/imagecodec/type_converter.h"
#include "hardware/imagecodec/image_codec_log.h"
namespace OHOS::ImagePlugin {
using namespace std;
vector<PixelFmt> g_pixelFmtTable = {
{GRAPHIC_PIXEL_FMT_YCBCR_420_SP, "NV12"},
{GRAPHIC_PIXEL_FMT_YCRCB_420_SP, "NV21"},
{GRAPHIC_PIXEL_FMT_YCBCR_P010, "NV12_10bit"},
{GRAPHIC_PIXEL_FMT_YCRCB_P010, "NV21_10bit"},
};
std::optional<PixelFmt> TypeConverter::GraphicFmtToFmt(GraphicPixelFormat format)
{
auto it = find_if(g_pixelFmtTable.begin(), g_pixelFmtTable.end(), [format](const PixelFmt& p) {
return p.graphicFmt == format;
});
if (it != g_pixelFmtTable.end()) {
return *it;
}
LOGW("unknown GraphicPixelFormat %{public}d", format);
return nullopt;
}
} // namespace OHOS::ImagePlugin

View File

@ -78,8 +78,8 @@ static bool FillFrameInfoForPixelConvert(AVFrame *frame, PixelFormatConvertParam
}
const OH_NativeBuffer_Plane &planeY = param.planesInfo->planes[0];
const OH_NativeBuffer_Plane &planeUV = param.planesInfo->planes[param.format == AV_PIX_FMT_NV21 ? 2 : 1];
IMAGE_LOGI("planeY offset: %{public}ld, columnStride: %{public}d, rowStride: %{public}d,"
" planeUV offset: %{public}ld, columnStride: %{public}d, rowStride: %{public}d",
IMAGE_LOGI("planeY offset: %{public}llu, columnStride: %{public}u, rowStride: %{public}u,"
" planeUV offset: %{public}llu, columnStride: %{public}u, rowStride: %{public}u",
planeY.offset, planeY.columnStride, planeY.rowStride,
planeUV.offset, planeUV.columnStride, planeUV.rowStride);
frame->data[0] = param.data + planeY.offset;

View File

@ -100,6 +100,20 @@
<option name="push" value="images/test_hw1.jpg -> /data/local/tmp/image" src="res"/>
</preparer>
</target>
<target name="heif_hw_decoder_test">
<preparer>
<option name="shell" value="mkdir -p /data/local/tmp/image/heif_test"/>
<option name="shell" value="mkdir -p /data/local/tmp/image/heif_test/1024x1024_nogrid"/>
<option name="push" value="images/heif_test/1024x1024_nogrid/19700301_230238_059_hevc_I0 -> /data/local/tmp/image/heif_test/1024x1024_nogrid" src="res"/>
<option name="push" value="images/heif_test/1024x1024_nogrid/19700301_230238_059_hevc_xps -> /data/local/tmp/image/heif_test/1024x1024_nogrid" src="res"/>
<option name="shell" value="mkdir -p /data/local/tmp/image/heif_test/1024x1024_grid_512x512_2x2"/>
<option name="push" value="images/heif_test/1024x1024_grid_512x512_2x2/19700301_225916_595_hevc_I0 -> /data/local/tmp/image/heif_test/1024x1024_grid_512x512_2x2" src="res"/>
<option name="push" value="images/heif_test/1024x1024_grid_512x512_2x2/19700301_225916_595_hevc_I1 -> /data/local/tmp/image/heif_test/1024x1024_grid_512x512_2x2" src="res"/>
<option name="push" value="images/heif_test/1024x1024_grid_512x512_2x2/19700301_225916_595_hevc_I2 -> /data/local/tmp/image/heif_test/1024x1024_grid_512x512_2x2" src="res"/>
<option name="push" value="images/heif_test/1024x1024_grid_512x512_2x2/19700301_225916_595_hevc_I3 -> /data/local/tmp/image/heif_test/1024x1024_grid_512x512_2x2" src="res"/>
<option name="push" value="images/heif_test/1024x1024_grid_512x512_2x2/19700301_225916_595_hevc_xps -> /data/local/tmp/image/heif_test/1024x1024_grid_512x512_2x2" src="res"/>
</preparer>
</target>
<target name="test_imagesystemadapter_java_maple">
<preparer>
<option name="push" value="images/test.png -> /data/local/tmp/image" src="res"/>